使用逗号运算符和可变模板参数包折叠表达式
Fold expression with comma operator and variadic template parameter pack
#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);
}
基于 c++ 运算符 doc,','
是一个 从左到右的运算符 。它的意思是 a, b, c, d
,意思是 (((a, b), c),d)
而不是 (a, (b, (c, d)))
。如果 a、b、c、d 是语句,这很重要。
然而,基于折叠表达式doc,对于','
应该使用一元左折叠。
我的问题是为什么我的代码中的两个语句都有效?不应该只有#2 工作吗?
还有如何理解...
和args
。和嵌套折叠表达式?
在链接页面中,您的#1 扩展如下:
((cout << args₀ << '\n'), ((cout << args₁ << '\n'), (cout << args₂ << '\n')));
删除一些重复以使其更清晰:
args₀, (args₁, args₂)
对于#2,扩展归结为:
(args₀, args₁), args₂
让我们来看看每一个的评价。
#1:
args₀ , (args₁, args₂)
^^^
带下划线的逗号计算左侧,打印 1
。然后右边求值,它求值args₁
的打印,打印test
,然后是args₂
的打印,打印5.6
.
#2:
(args₀, args₁) , args₂
^^^
带下划线的逗号计算左侧。这会触发另一个逗号的计算,它计算 args₀
的打印,打印 1
,然后计算 args₁
的打印,打印 test
。现在,带下划线的逗号已完成评估左侧并评估右侧,打印 5.6
.
如您所见,尽管括号的分组不同,但两者对每个参数的求值顺序相同。
请注意,一般来说,这可能并不总是成立。一些运算符,例如 +
,不像逗号运算符那样有保证的评估顺序。如果使用这样的运算符而不是逗号来连接打印表达式,编译器最终可以选择以任何顺序评估各个参数打印。
假设我们在一个二元运算符上折叠 3 个表达式,使用一元折叠。我们这里有两个选项:(xs @ ...)
(一元右折叠)和 (... @ xs)
(一元左折叠)。
(xs @ ...)
扩展为 (a @ (b @ c))
(... @ xs)
扩展为 ((a @ b) @ c)
关于表达式 a @ (b @ c)
和 (a @ b) @ c
之间的区别,我们能说些什么?如果 @
在这些类型上是 associative,那么这两个表达式是相同的。这就是联想的意思。如果您有一个整数参数包,那么一元左折叠 +
和一元右折叠 +
将具有相同的值(模数溢出),因为加法是关联的。另一方面,减法不是结合的。 (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';
#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);
}
基于 c++ 运算符 doc,','
是一个 从左到右的运算符 。它的意思是 a, b, c, d
,意思是 (((a, b), c),d)
而不是 (a, (b, (c, d)))
。如果 a、b、c、d 是语句,这很重要。
然而,基于折叠表达式doc,对于','
应该使用一元左折叠。
我的问题是为什么我的代码中的两个语句都有效?不应该只有#2 工作吗?
还有如何理解...
和args
。和嵌套折叠表达式?
在链接页面中,您的#1 扩展如下:
((cout << args₀ << '\n'), ((cout << args₁ << '\n'), (cout << args₂ << '\n')));
删除一些重复以使其更清晰:
args₀, (args₁, args₂)
对于#2,扩展归结为:
(args₀, args₁), args₂
让我们来看看每一个的评价。
#1:
args₀ , (args₁, args₂)
^^^
带下划线的逗号计算左侧,打印 1
。然后右边求值,它求值args₁
的打印,打印test
,然后是args₂
的打印,打印5.6
.
#2:
(args₀, args₁) , args₂
^^^
带下划线的逗号计算左侧。这会触发另一个逗号的计算,它计算 args₀
的打印,打印 1
,然后计算 args₁
的打印,打印 test
。现在,带下划线的逗号已完成评估左侧并评估右侧,打印 5.6
.
如您所见,尽管括号的分组不同,但两者对每个参数的求值顺序相同。
请注意,一般来说,这可能并不总是成立。一些运算符,例如 +
,不像逗号运算符那样有保证的评估顺序。如果使用这样的运算符而不是逗号来连接打印表达式,编译器最终可以选择以任何顺序评估各个参数打印。
假设我们在一个二元运算符上折叠 3 个表达式,使用一元折叠。我们这里有两个选项:(xs @ ...)
(一元右折叠)和 (... @ xs)
(一元左折叠)。
(xs @ ...)
扩展为 (a @ (b @ c))
(... @ xs)
扩展为 ((a @ b) @ c)
关于表达式 a @ (b @ c)
和 (a @ b) @ c
之间的区别,我们能说些什么?如果 @
在这些类型上是 associative,那么这两个表达式是相同的。这就是联想的意思。如果您有一个整数参数包,那么一元左折叠 +
和一元右折叠 +
将具有相同的值(模数溢出),因为加法是关联的。另一方面,减法不是结合的。 (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';