函数返回的值的"左值"和"右值"背后的逻辑是什么

Sou*_*mar 2 c++

在下面的代码中,我期望函数Foo f(Foo& x)返回一个rvalue,令我惊讶的是它没有.所以我开始测试一些案例,找出返回值的"左值"和"rvlaueness"背后的逻辑.事实证明,返回类型为class的命名变量是左值.也许我错过了一些东西

#include <iostream>

class Foo
{
    int val;
public:
    Foo(int value=10)
    {
        val = value;
    };

    Foo(Foo& other)
    {
        std::cout << "copy ctor used" << std::endl;
        val = other.val;
    };
    Foo(Foo&&)
    {
        std::cout << "move ctor used" << std::endl;
    };
    Foo operator=(Foo& other) 
    {

        std::cout << "copy assign used" << std::endl;
        val = other.val;
        return *this;
    }
    Foo operator=(Foo&& a)
    {
        std::cout << "move assign used" << std::endl;
        return *this;
    }
    void set(int value) { val = value; };
    int get() { return val; };

};


int z = 20;;
int case1()
{
    return z;
};

int case2(int x)
{
    return x;
};

Foo case3(Foo x)
{
    return x;
};

Foo case4(Foo& x)
{
    return x;
};

Foo y;
Foo case5()
{
    return y;
}


Foo& case6(Foo& x)
{
    return x;
};

Foo case7()
{
    Foo x;
    return x;
}



int main()
{

    /*case1() = 50;*/ // as expected: not OK the return value is not an lvalue

    int c = 40;
    /*case2(c) = 50; */ // as expected: not OK the return value is not an lvalue

    Foo a;
    Foo b = 30;
    case3(a) = b; // OK: not at all expected the return value is an lvalue
                  // I don't see the difference with case 2
                  //copy construction of the arguement and return value
                  //copy assing of b

    case4(a) = b; //OK: behaving exactly like case 3 except for the copy construction
                  // of the argument and the return value
                  // copy assing taking place as expected

    case5() = b; // same as case 3

    case6(a) = b; //just copy assing taking place
                  // the same refrence to a is returned 

    case7() = b;  // same as before
    std::cin.get();
}

Run Code Online (Sandbox Code Playgroud)

Bri*_*ian 5

您无法通过检查调用哪些构造函数来确定返回值的值类别,因为编译器在某些情况下可以删除复制和移动,消除用于保存返回值(NRVO)的局部变量,并将副本转换为移动.

但是,值类别规则很简单:

  • 如果函数声明的返回类型是左值引用,则返回值是左值.
  • 如果函数声明的返回类型是右值引用,则返回值是xvalue.
  • 否则,返回值是prvalue.

(函数有一个例外:函数值总是左值.)


Nat*_*ica 5

在所有case函数中,除了case6()按值返回,因此你有一个rvalue表达式.

原因case1()case2()不起作用但其他人之所以这样做是因为内置类型的赋值运算符仅适用于左值.用户定义的类型没有这样的限制,除非您自己通过向赋值运算符添加引用限定符来添加它.

将复制赋值运算符更改为

Foo operator=(Foo& other) & //<- notice the & at the end
{
    std::cout << "copy assign used" << std::endl;
    val = other.val;
    return *this;
}
Run Code Online (Sandbox Code Playgroud)

将导致除了case6()不编译的所有情况,因为尾部&表示您只能在左值上调用复制赋值运算符.