【问题标题】:Is the right operand of an assignment always converted to an rvalue?赋值的右操作数是否总是转换为右值?
【发布时间】:2018-04-25 12:34:09
【问题描述】:

我想澄清一下这个特殊情况:

class Test
{
    Test& operator=(const Test& copy)
    {
      ...
    }

    Test() = default;
}

Test a;
Test b;
b = a; //Is "a" converted to an rvalue?

"a" 是一个左值,但它现在是赋值的右手操作数。这是否意味着它被转换为右值?

此代码无法编译:

class Test
{
    Test& operator=(Test&& copy) 
    {
      ...
    }

    Test() = default;
}

Test a;
Test b;
a = b; //Error

但是这个可以:

class Test
{
    Test& operator=(Test& copy) 
    {
      ...
    }

    Test() = default;
}

Test a;
Test b;
a = b; //OK

由于左值引用不能绑定到右值,这是否意味着这里没有发生转换?如果是这种情况,什么时候会发生隐式左值到右值的转换? (除了基本类型的 operator+ 的情况)

【问题讨论】:

  • Test b = a; 不是赋值,而是初始化。它不使用operator=,而是使用构造函数。不,您的示例中没有左值到右值的转换。
  • 只要在需要右值的上下文中使用左值,就会发生隐式左值到右值的转换。复制构造函数不能是这样的上下文 - 对于类类型,左值到右值的转换意味着复制,所以如果复制构造函数需要这样的转换,你将有一个无限递归。
  • @IgorTandetnik 对不起,你是对的,我已经更正了这个问题。这种情况适用于赋值和初始化。
  • 你可以look at the AST看看编译器是如何解释这个的。它显示了直接进入构造函数的左值。
  • void f(Test t) {} Test a; f(a); 此处的函数调用需要左值到右值的转换,因为该函数按值获取其参数。

标签: c++ lvalue-to-rvalue


【解决方案1】:

这是左值,b = a; 调用复制操作符

class Test
{
    Test& operator=(const Test& copy)
    {
      ...
    }

    Test() = default;
}

Test a;
Test b;
b = a; //Is "a" converted to an rvalue?

错误,因为你的复制构造函数和复制操作符不是由编译器生成的,因为声明了移动操作符

class Test
{
    Test& operator=(Test&& copy) 
    {
      ...
    }

    Test() = default;
}

Test a;
Test b;
a = b; //Error

如果你想获得右值,你可以这样写:

Test a;
Test b;
a = Test(); //Uses move operator

【讨论】:

  • 可能想要加入调用 std::move() 确实会进行转换。
【解决方案2】:

在您的情况下没有左值到右值的转换(忽略operator= 定义中的代码)。

这里你使用了重载的操作符,根据[expr.pre]/2

重载的运算符遵循[expr.compound]中指定的语法和计算顺序规则,但操作数类型和值类别的要求被函数调用规则所取代。

所以它不需要正确的操作数是纯右值,因此没有左值到右值的转换。

【讨论】:

    【解决方案3】:

    "a" 是一个左值,但它现在是赋值的右手操作数。这是否意味着它被转换为右值?

    简而言之,是的。在 C++ 标准的所有版本和草案中,其中一种标准转换称为“左值到右值”转换。

    这适用于您描述的第一种情况。

    由于左值引用不能绑定到右值,这是否意味着这里没有发生转换?如果是这种情况,什么时候会发生隐式左值到右值的转换? (除了基本类型的 operator+ 的情况)

    在第二种情况下,您已声明/定义了一个接受右值引用的operator=(Test &&),这会抑制operator=(const Test &) 的生成。在 C++11 及更高版本中,声明 operator=(Test &&) 而没有 operator=(Test &)operator(const Test &) 之一会阻止在右侧使用左值(如您的示例中,a = b)。

    这间接意味着在这种情况下不使用左值到右值的转换。

    【讨论】:

    • 感谢您的回答。一个怀疑的想法。如果在第一种情况下发生转换,那应该意味着类的临时对象中有一个副本,但似乎没有发生。
    • 我不得不承认,我自己也不明白这一点。 Here 我能够让 Clang 承认左值到右值的转换。但是对于任何其他示例,我都看不到 AST 中的转换。
    • 这可能意味着创建一个临时对象 - 例如,如果您将 Test 按值传递给函数。但是,如果转换产生对命名对象的引用,则不需要(必然)创建临时对象。
    • @wally 我发现标准对于这种隐式转换非常混乱。但是我相信 Igor 给出的示例“Test t”也是一个左值,所以不需要转换(只是一个副本)。但是在您的示例中,函数的返回值是prvalue,因此clang正在进行转换(复制+转换)。
    • @A.S.好的,这是有道理的。 Here 更清楚一点。
    猜你喜欢
    • 2013-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-09-28
    • 2021-04-22
    相关资源
    最近更新 更多