C++17 Fold Expressions
C++17 Fold Expressions
本文介绍C++17新特性折叠表达式。文章示例代码通过MinGW编译,宏__cplusplus=201703
下面我们从一个模板函数sum开始,介绍折叠表达式。
用折叠表达式简化以前的代码
C++11引入了变长参数模板,我们想求变长参数的和,可以这样写模板函数
template <typename T>
auto sum(T arg){
return arg;
}
template <typename T1, typename... T>
auto sum(T1 arg, T... args){
return arg + sum(args...);
}
int main() {
std::cout<<sum(1,2,3)<<std::endl;
return 0;
}
注:这里用到了auto推导,C++14编译没问题。用C++11编译的读者记得在函数签名后面加"-> T"
为了完成sum这个函数,我们需要写2个函数。一个进行递归,一个作为递归返回条件。这种模板的递归是在编译时发生的,遇到复杂的递归会大大加大编译时间。有没有办法写一个模板函数,同时又能实现sum功能呢?
有,就是折叠表达式
我们看下折叠表达式的写法
template <typename... T>
auto sum_folder(T... args) {
return (... + args);
}
int main() {
std::cout<<sum_folder(1,2,3)<<std::endl;
return 0;
}
这里折叠表达式会将sum_folder(1,2,3)扩展成(1+(2+3))
如何使用折叠表达式
看了上面的例子,相信读者对折叠表达式已经有了直观的认识。下面我们详细介绍下折叠表达式的使用。
一元折叠
假设参数是args,操作符是op。一元折叠有两种情况:左折叠和右折叠
- unary left fold: (... op args) expends to ((arg1 op arg2) op arg3) + ...
- unary right fold: (args op ...) expends to arg1 op (arg2 op ... (argN-1 op argN))
举2个例子方便理解
左折叠
template<typename... T>
string combine_str_left(T... args) {
return (... + args);
}
string s("head");
// expand to (s+" ") + "tail"
std::cout<<combine_str_left(s," ","tail")<<std::endl;
右折叠
template<typename... T>
string combine_str_right(T... args) {
return (args + ...);
}
右折叠就不能combine_str_right(s," ", "tail")这么写,因为扩展开来是s + (" " + "tail")显然两个字面值是不能直接相加的。所以比起右折叠,左折叠用的比较多。
二元折叠
- binary left fold: (value op ... op args) expand to ((value op arg1) op arg2) op ...
- binary right fold: (args op ... op value) expand to args1 op (arg2 op ... ( argN op value))
同样举个左折叠的例子
template<typename... T>
void print(const T&... args) {
(std::cout << ... << args);
}
print("a","b", "c"); // print abc
想让各个参数中间打印一个空格,可以使用一个lambda处理下
template<typename First, typename... T>
void print(const First& first,const T&... args) {
std::cout<<first;
auto whiteSpaceWrapper = [](const auto& arg){
std::cout<<" ";
return arg;
};
(std::cout << ... << whiteSpaceWrapper(args));
}
lambda表达式使用fold expressions
折叠表达式不仅用于模板,也可以用在lambda表达式里面
auto print = [](auto&... args){ (std::cout << args << ... ) << '\n';};
print("first", "second", "third");