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

时间:2017-10-04 08:05:33

标签: c++ c++17

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

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

int main() {
    sum(10, 2, 2);
}

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

4 个答案:

答案 0 :(得分:7)

fold-expression具有与简单地写出运算符的N-1个应用程序相同的语义(其中N是包中元素的数量)。例如,sum(10, 2, 2)将生成(10 + 2) + 2。请参阅[temp.variadic]/9

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

答案 1 :(得分:4)

不一定是。这一切都取决于运营商。如果您使用+=代替(有点人为的使用),结果表达式将不是prvalue,而是左值。

澄清:

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

由于+=的结果是指定左手操作数的左值(为了简单起见,我将折扣疯狂的重载),折叠表达式也最终产生左值。所以最后的任务完全有效,虽然很臭。

答案 2 :(得分:2)

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

template<typename... Args>
void foo(Args&... args)
{
    (args, ...) = 42;
}

void bar()
{
    int x, y, z;
    foo(x, y, z);
}

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

答案 3 :(得分:2)

我非常喜欢@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将采用 exact < / em>总和的类型。

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

((E 1 op E 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&作为回报,这绝对不是一个公关:

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

打印

  

全局字符串