使用逗号运算符和可变参数模板参数包折叠表达式

Ke *_*ang 11 c++ fold operator-keyword variadic-templates c++17

#include<iostream>
using namespace std;

template<typename ...Args>
void output_argus(Args&&... args) 
{
    ((cout << args << '\n'), ...);    // #1
    (... , (cout << args << '\n'));   // #2
}


int main()
{
    output_argus(1, "test", 5.6f);
}
Run Code Online (Sandbox Code Playgroud)

基于c ++ operator doc,','是一个从左到右的运算符.意思a, b, c, d是意义(((a, b), c),d)不是(a, (b, (c, d))).如果a,b,c,d是陈述,这一点很重要.

但是,基于fold表达式doc,','应该使用一元左折.

我的问题为什么我的代码中的两个语句都有效?不应该只#2工作吗?以及如何理解...args.和嵌套的折叠表达式?

Bar*_*rry 6

假设我们在一个二元运算符上折叠3个表达式,并使用一元折叠.我们在这里有两个选择:( (xs @ ...)一元右折)和(... @ xs)(一元左折).

(xs @ ...) 扩展到 (a @ (b @ c))

(... @ xs) 扩展到 ((a @ b) @ c)

我们可以说的表达式之间有什么区别a @ (b @ c)(a @ b) @ c?如果这些类型@关联的,那么这两个表达式是相同的.这就是联想意味着什么.如果你有一个整数参数包,那么一元左折叠+和一元右折叠+将具有相同的值(模数溢出),因为加法是关联的.另一方面,减法不是关联的.(xs - ...)并且(... - xs)意味着非常不同的事情

同样,,C++中的运算符是关联的.将表达式括起来的方式无关紧要.((a, b), c)(a, (b, c))评估和丢弃a,然后评估和丢弃b,然后评估c,这是结果.更容易看出你是否将表达式简化为字母为什么会出现这种情况.

其结果是,无论是((cout << args << '\n'), ...)(... , (cout << args << '\n'))做同样的事情,他们都实际上意味着:

cout << args1 << '\n';
cout << args2 << '\n';
// ...
cout << argsN << '\n';
Run Code Online (Sandbox Code Playgroud)


chr*_*ris 5

从链接页面,您的 #1 扩展如下:

((cout << args? << '\n'), ((cout << args? << '\n'), (cout << args? << '\n')));
Run Code Online (Sandbox Code Playgroud)

删除一些重复以使其更清晰:

args?, (args?, args?)
Run Code Online (Sandbox Code Playgroud)

对于#2,扩展归结为:

(args?, args?), args?
Run Code Online (Sandbox Code Playgroud)

让我们来看看每个人的评价。

#1:

args?  ,  (args?, args?)
      ^^^
Run Code Online (Sandbox Code Playgroud)

带下划线的逗号计算左侧,打印1. 然后右侧被评估,其评估的打印args?,打印test,则打印args?,打印5.6

#2:

(args?, args?)  ,  args?
               ^^^
Run Code Online (Sandbox Code Playgroud)

带下划线的逗号计算左侧。这会触发对另一个逗号的评估,该逗号评估args?,printing 的打印1,然后评估args?,printing 的打印test。现在带下划线的逗号已完成评估左侧并评估右侧,打印5.6.

如您所见,尽管括号的分组不同,但两者都会对每个单独的参数产生相同的评估顺序。

请注意,通常情况下,这可能并不总是成立。某些运算符(例如 )+没有像逗号运算符那样有保证的计算顺序。如果使用这样的运算符而不是逗号来连接打印表达式,编译器最终可以选择以任何顺序评估单个参数打印。