折叠表达式的值类别是否始终是 prvalue?

Is the value category of a fold expression always prvalue?

本文关键字:prvalue 是否 表达式 折叠      更新时间:2023-10-16

折叠表达式总是 prvalue 吗?这是对的吗?

template<typename... Args>
auto sum(Args... args) {
auto fold = (... +  args);
return fold;
}
int main() {
sum(10, 2, 2);
}

我真的只对上面示例中(... + args)的折叠表达式感兴趣。

折叠表达式与简单地写出运算符的 N - 1 个应用程序具有相同的语义(其中 N 是包中的元素数)。例如,sum(10, 2, 2)将产生(10 + 2) + 2.参见[温度变化]/9。

一般来说,这可能是也可能不是价值。用+折叠 2 个或更多数值将始终产生一个 prvalue,因为内置的+运算符会产生一个 prvalue,但如果包中只有一个元素args,那么(... + args)的意思与简单地通过其假设名称提及该元素的含义相同,因此结果将是一个左值。当然,您也可以与其他(可能过载的)运算符折叠,这可能会产生glvalue。

不必如此。这一切都取决于运营商。如果要改用+=(有点人为的用法),则生成的表达式将不是 prvalue,而是左值。

澄清一下:

template<typename... Args>
auto sum(Args... args) {
auto fold = (... +=  args) = 3;
return fold;
}

由于+=的结果是一个指定左操作数的左键(为了简单起见,我忽略了疯狂的重载),折叠表达式最终也会产生一个左键。所以最后的作业是完全有效的,虽然很臭。

折叠表达式不会影响表达式的值类别,它实际上只是编译器为您扩展参数包

template<typename... Args>
void foo(Args&... args)
{
(args, ...) = 42;
}
void bar()
{
int x, y, z;
foo(x, y, z);
}

有效,因为(args, ...)具有类型int&

我真的很喜欢 @Brian 和 @Passer By 的答案; 它具有与使用运算符扩展参数包相同的语义。我想添加一个(人为的)示例来演示这意味着什么。

让我们先修改 sum 函数以使用decltype(auto)和转发语义:

template<typename... Args>
decltype(auto) sum(Args&&... args) {
decltype(auto) fold = (... +  std::forward<Args>(args));
return fold;
}

在功能上仍然相同,只是现在sum完全返回fold的类型,并且fold将采用求和的确切类型。

我们的折叠表达式称为一元左折叠,其扩展如下所示:

((E1opE 2) op ...) op EN

更具体地说,假设Args可以被视为长度N数组:

((Args[0] + Args[1]) + ...) + Args[N-1]

这基本上与

Args[0] + Args[1] + ... + Args[N-1]

因此,既然我们已经从整个事情中去除了一些神秘主义,那么不需要太多时间就能看到表达式的类型实际上只是将+运算符两侧遇到的任何类型的结果相加的结果类型。

我可以设计以下具有一些奇怪的加法语义的Foo类型:

const std::string global = "Global string";
struct Foo{
Foo(int){}
};
const std::string& operator +(const Foo&, const Foo&){
return global;
}
const std::string& operator +(const std::string& _str, const Foo&){
return _str;
}

现在,当我调用sum以获取Foo实例时,我将收到一个const std::string&作为回报,这绝对不是 prvalue:

int main() {
std::cout << sum(Foo{10}, Foo{2}, Foo{2}) << std::endl;
}

指纹

全局字符串