522 字
3 分钟
14模板折叠
模板折叠
模板折叠用于在模板中,将可变长度参数,向左或者向右展开,并进行运算
- 左折叠:
(init op ... op args)
or... op args
- 右折叠:
(args op ... op init)
orargs op ...
其中,args
表示输入的参数;op
为运算符(操作符),对args进行运算;...
表示占位符,表示折叠后的内容。
模板折叠代码需要先展开,再根据展开的结果运算。
例子:
// 展开为表达式(... + args);-> (((arg1 + arg2) + arg3) + arg4);-> (arg1 + arg2 + arg3 + arg4);
// 展开为函数参数列表(std::max)(first, ..., args);-> (std::max)((((first, arg1), arg2), arg3), arg4)-> (std::max)(first, arg1, arg2, arg3, arg4);
由于我们的代码一般都是从左往右执行,因此左折叠使用的较多
但是当需要从右往左执行的情况时(如,字符串从右往左),需要使用到右折叠。
甚至左折叠和右折叠混合使用。
展开
左折叠的展开(... op args)
后,从左向右运行。
(((arg1 op arg2) op arg3) op arg4)
// 如果是init op ... op args,展开结果为
((((init op arg1) op arg2) op arg3) op arg4)
右折叠展开args... op
后,从右向左运行。
(arg1 op (arg2 op (arg3 op arg4)))
// 如果是args op ... op init,展开结果为
(arg4 op (arg3 op (arg2 op (arg1 op init))))
根据左右折叠的执行方向不同,因此需要根据运算符的结合顺序选择合适的折叠方式。
例如:如果op
为<<
运算符,因为<<
运算符是左结合的,使用右折叠会存在一些问题,在visual studio
中无法通过编译。
#include <iostream>
template<typename ... Args>auto printLeftStr(const Args& ... args) -> decltype(auto) { return (std::cout << ... << args);}
/*错误,args无法与<<结合*/
//template<typename ... Args>//auto printRightStr(const Args& ... args) -> decltype(auto) {// return (args << ... << std::cout);//}
int main() { printLeftStr("1", "2", "Hello", "World", "!", "3"); //printRightStr("1", "2", "Hello", "World", "!", "3");}
上述代码中,
(args << ... << std::cout);
展开后为:
(arg1 << (arg2 << (arg3 << (arg4 << std::cout))))
因为<<
为左结合,展开后优先和arg4
结合,由于arg4
不一定是ostream
类型,因此会报错。