前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++17 fold expression

C++17 fold expression

作者头像
恋喵大鲤鱼
发布2018-09-27 16:03:41
1.7K0
发布2018-09-27 16:03:41
举报
文章被收录于专栏:C/C++基础C/C++基础

1.简介

C++11增加了一个新特性变参模板(variadic template),它可以接受任意个模版参数,参数包不能直接展开,需要通过一些特殊的方法,比如函数参数包的展开可以使用递归方式或者逗号表达式,在使用的时候有点难度。C++17解决了这个问题,通过fold expression(折叠表达式)简化对参数包的展开。

2.语法形式

折叠表达式共有四种语法形式,分别为一元的左折叠和右折叠,以及二元的左折叠和右折叠。

代码语言:javascript
复制
一元左折叠(unary left fold)
( ... op pack )
一元左折叠(... op E)展开之后变为 ((E1 op E2) op ...) op En

一元右折叠(unary right fold)
( pack op ... )
一元右折叠(E op ...)展开之后变为 E1 op (... op (EN-1 op En))

二元左折叠(binary left fold) 
( init op ... op pack )
二元左折叠(I op ... op E)展开之后变为 (((I op E1) op E2) op ...) op En

二元右折叠(binary right fold) 
( pack op ... op init )
二元右折叠(E op ... op I)展开之后变为 E1 op (... op (EN−1 op (EN op I)))

(1)语法形式中的op代表运算符,pack代表参数包,init代表初始值。 (2)不指定初始值的为一元折叠表达式,而指定初始值的为二元折叠表达式。 (3)初始值在右边的为右折叠,展开之后从右边开始折叠。而初始值在左边的为左折叠,展开之后从左边开始折叠。 (4)当一元折叠表达式中的参数包为空时,只有三个运算符(&& || 以及逗号)有缺省值,其中&&的缺省值为true,||的缺省值为false,逗号的缺省值为void()。 fold expression支持32种操作符:

代码语言:javascript
复制
+ - * / % ^ & | = < > << >> += -= *= /= %= ^= &= |= <<= >>= == != <= >= && || , .* ->*

3.使用实例

(1)一元右折叠 从表达式右边开始fold,看它是left fold还是right fold我们可以根据参数包…所在的位置来判断,当参数包…在操作符右边的时候就是right fold,在左边的时候就是left fold。示例如下:

代码语言:javascript
复制
template<typename... Args>
auto add_val(Args&&... args) 
{
    return (args + ...);
}

auto t = add_val(1,2,3,4); //10

(2)一元左折叠 对于+这种满足交换律的操作符来说,left fold和right fold是一样的,比如上面的例子你也可以写成left fold。

代码语言:javascript
复制
template<typename... Args>
auto add_val(Args&&... args) 
{
    return (... + args);
}

auto t = add_val(1,2,3,4); //10

对于不满足交换律的操作符来说就要注意了,比如减法,下面的right fold和left fold的结果就不一样。

代码语言:javascript
复制
template<typename... Args>
auto sub_val_right(Args&&... args) 
{
    return (args - ...);
}


template<typename... Args>
auto sub_val_left(Args&&... args) 
{
    return (... - args);
}

auto t = sub_val_right(2,3,4); //(2-(3-4)) = 3
auto t1 = sub_val_left(2,3,4); //((2-3)-4) = -5

(3)二元右折叠 二元fold的语义和一元fold的语义是相同的,参数包…在左即二元左折叠,参数包…在右即右折叠。下面看一个二元右折叠的例子。

代码语言:javascript
复制
template<typename... Args>
auto sub_one_left(Args&&... args)
{
    return (1 - ... - args);
}
auto t = sub_one_right(2,3,4);//(2-(3-(4-1))) = 2

(4)二元左折叠

代码语言:javascript
复制
template<typename... Args>
auto sub_one_right(Args&&... args) 
{
    return (args - ... - 1);
}
auto t = sub_one_left(2,3,4);// (((1-2)-3)-4) = -8

(5)comma fold 在C++17之前,我们经常使用逗号表达式结合列表初始化的方式对参数包进行展开,比如像下面这个例子:

代码语言:javascript
复制
template<typename T>
void print_arg(T t)
{
    std::cout << t << std::endl;
}

template<typename... Args>
void print2(Args... args)
{
int a[] = { (print_arg(args), 0)... };
//或者
    //std::initializer_list<int>{(print_arg(args), 0)...};
}

这种写法比较繁琐,用fold expression就会变得很简单了。

代码语言:javascript
复制
//unary right fold
template<typename... Args>
void print3(Args... args)
{
    (print_arg(args), ...);
}

//unary left fold
template<typename... Args>
void print3(Args... args)
{
    (..., print_arg(args));
}

unary right fold和unary left fold,对于comma来说两种写法是一样的,参数都是从左至右传入print_arg函数。当然,我们也可以通过binary fold实现:

代码语言:javascript
复制
template<typename ...Args>
void printer(Args&&... args)
 {
    (std::cout << ... << args) << '\n';
}
注意,下面的写法是不合法的,根据binary fold的语法,参数包…必须在操作符中间。

template

参考文献

[1]C++17中那些值得关注的特性(上) [2]C++17尝鲜:fold expression(折叠表达式)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年08月28日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.简介
  • 2.语法形式
  • 3.使用实例
  • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档