C++17中的折叠表达式实现

 更新时间:2020年11月18日 10:25:42   作者:ztenv  
这篇文章主要介绍了C++17中的折叠表达式实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

C++11 提供了可变模板参数包, 使函数可以接受任意数量的参数. 但在 C++11中展开参数包稍显麻烦, 而 C++17 的折叠表达式使得展开参数包变得容易, 其基本语法是使用 (…) 的语法形式进行展开。

支持的操作符

C++17中,折叠表达式支持 32 个操作符:

 +, -, *, /, %, ^, &, |, =, <,
>, <<, >>, +=, -=, *=, /=, %=, ^=, &=, |=, <<=,
>>=,==, !=, <=, >=, &&, ||, ,, .*, ->*.

折叠分类

折叠位置

1.左折叠
2.右折叠

操作数个数
1.一元折叠
2.二元折叠

例 1: 左折叠

template <typename ... Ts>
auto sum(Ts ... ts)
{
  return (... + ts);
}

int res{sum(1, 2, 3, 4, 5)};
std::string a{"Hello "};
std::string b{"World"};
std::string str_res {sum(a, b)};//a+b的结果

例 2: 右折叠

template <typename ... Ts>
auto sum(Ts ... ts)
{
  return (ts + ...);
}

例 1 中, 参数包 … 位于操作符的左侧,故尔称为左折叠。 如果 …位于操作符右侧,则称为右折叠,如例 2 所示。就例 1 与例 2 而言,左折叠与右折叠的效果是相同的。

int res {sum(1, 2, 3, 4, 5)};
//左折叠的展开方式为

 1 + (2 + (3 + (4 + 5))),
//右折叠的展开方式为

(((1 + 2) + 3) + 4) + 5
//在例 1 与 例 2 中,如果参数包包含的参数数量为 0,即为空包,会产生编译错误,如

int the_sum {sum()};
/*大致的错误输出如下

In instantiation of 'auto sum(Ts ...) [with Ts = {}]':
error: fold of empty expansion over operator+
   return (... + ts);
*/

若要解决空参数包的编译错误,针对例 1,可以加上一个数值 0,可以解决编译错误又可以使得语义不变,这个 0 相当于缺省值。通过加上一个数值,折叠就变成了二元折叠,如例 3 所示。

例 3: 二元折叠

template <typename ... Ts>
auto sum(Ts ... ts)
{
  // 二元右折叠
  return (ts + ... + 0);

  // 二元左折叠
  // return (0 + ... + ts);
}

此时对于 int res{sum(1, 2, 3, 4, 5)};折叠的展开方式为
1 + (2 + (3 + (4 + (5 + 0))))

空参数包

空参数包就是参数包中不含任何参数。对于大多数操作符,空参数包将会引发编译错误。对于 && 或 ||,空参数包是合法的,其中 && 的展开结果为 true,||的展开结果为 false。在逗号 , 操作符中,空参数包也合法,展开为 void()。

例 4: 计算指定区间内包含指定数值的个数

template <typename R, typename ... Ts>
auto count(const R& range, Ts ... ts)
{
  return (std::count(std::begin(range), std::end(range), ts) + ...);
}
...
std::vector<int> v {1, 2, 3, 4, 5};
count(v,     2, 5);     // returns 2
count(v,     100, 200);   // returns 0
count("abcdefg", 'x', 'y', 'z'); // returns 0
count("abcdefg", 'a', 'd', 'f'); // returns 3

例 5: 检查插入多个元素是否成功

template <typename T, typename ... Ts>
bool insert_all(T &set, Ts ... ts)
{
  return (set.insert(ts).second && ...);
}
...
std::set<int> my_set {1, 2, 3};
insert_all(my_set, 4, 5, 6); // Returns true, my_set 值为 {1, 2, 3, 4, 5, 6}
insert_all(my_set, 7, 2, 8); // Returns false, my_set 值为 {1, 2, 3, 4, 5, 6, 7}
               // 插入 2 时出错, 8 不会被插入

最后

1.对于一元右折叠 (E op …) 具体展开为 E1 op (… op (EN-1 op EN))。
2.对于一元左折叠 (… op E) 具体展开为 (( E1 op E2) op …) op En。
3.对于二元右折叠 (E op … op I) 具体展开为 E1 op (… op (EN-1 op (EN op I)))。
4.对于二元左折叠 (I op … op E) 具体展开为 (((I op E1) op E2) op …) op E2。

左折叠与右折叠的语义并非总是相同的。比如对于加法和乘法,左折叠与右折叠的语义是相同的,但是对于减法与除法,其语义是不同的。

例 6: 左右折叠不同语义

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

template<typename... Args>
auto right_sub(Args&&... args) {
  return (args - ...);
}
...
auto a = left_sub(2, 3, 4); // ((2 - ) -3 ) - 4) = -5
auto b = right_sub(2, 3, 4); // (2 - (3 - 4)) = 3

到此这篇关于C++17中的折叠表达式实现的文章就介绍到这了,更多相关C++17 折叠表达式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • 一文搞懂C++中继承的概念与使用

    一文搞懂C++中继承的概念与使用

    我们都知道面向对象语言的三大特点是:**封装,继承,多态。**之前在类和对象部分,我们提到了C++中的封装,那么今天呢,我们来学习一下C++中的继承
    2022-07-07
  • C++中Semaphore内核对象用法实例

    C++中Semaphore内核对象用法实例

    这篇文章主要介绍了C++中Semaphore内核对象用法实例,有助于深入了解信号量(Semaphore)的基本用法,需要的朋友可以参考下
    2014-10-10
  • 利用C++实现通讯录管理系统的完整代码

    利用C++实现通讯录管理系统的完整代码

    通讯录是一个可以记录亲人、好友信息的工具,下面这篇文章主要给大家介绍了关于利用C++实现通讯录管理系统的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • C++中const用于函数重载的示例代码

    C++中const用于函数重载的示例代码

    这篇文章主要介绍了C++中const用于函数重载的相关资料,需要的朋友可以参考下
    2017-09-09
  • C++基于Directx MMX实现的图像灰度转换代码

    C++基于Directx MMX实现的图像灰度转换代码

    这篇文章主要介绍了C++基于Directx MMX实现的图像灰度转换代码,需要的朋友可以参考下
    2014-08-08
  • C++各种输出数据类型详解

    C++各种输出数据类型详解

    这篇文章主要介绍了C++各种输出数据类型,在C++中,可以使用cout对象和插入运算符<<输出各种数据类型,包括整数类型、浮点数类型、字符类型、字符串类型和布尔类型,需要的朋友可以参考下
    2023-06-06
  • C语言实现颠倒栈的方法

    C语言实现颠倒栈的方法

    这篇文章主要介绍了C语言实现颠倒栈的方法,是针对数据结构中栈的常见操作技巧,需要的朋友可以参考下
    2014-09-09
  • C++二级指针和指针的引用

    C++二级指针和指针的引用

    这篇文章主要介绍了C++二级指针和指针的引用,下文举例实现形参指针改变实参指针所指向的对象,需要的小伙伴可以参考一下,希望对你的学习有所帮助
    2022-03-03
  • C++常量详解一(常量指针与常量引用的初始化)

    C++常量详解一(常量指针与常量引用的初始化)

    这篇文章主要介绍了C++常量详解一(常量指针与常量引用的初始化),需要的朋友可以参考下
    2017-06-06
  • C++之谈谈构造函数的初始化列表

    C++之谈谈构造函数的初始化列表

    构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用,这篇文章详细介绍了构造函数的初始化列表,文章中有详细的示例代码,感兴趣的同学可以参考阅读
    2023-04-04

最新评论