7
class Test {

public:

    int n1;

};

Test func() {

    return Test();

}

int main() {

    func() = Test();

}

这对我来说没有意义。如何以及为什么允许这样做?它是未定义的行为吗?如果一个函数返回一个右值,那么如何将一个右值设置为另一个右值?如果我用任何原始类型尝试这个,它会像我预期的那样给我一个错误。

我知道左值是内存中的一个位置,那么该函数是否会创建一个临时左值(右值?)并将其分配给另一个左值?有人可以解释这种语法是如何工作的吗?

4

2 回答 2

7

函数调用表达式的值类别实际上是一个右值。

实际上,您可能不会在右值原语上调用复制赋值运算符。C 中右值的历史定义实际上是它们可能不在分配左侧的区别。

不过,类的赋值运算符有点不同。它们是常规的成员函数。并且没有规则阻止调用右值的成员函数。事实上,当函数有副作用时,它通常非常有用。

它是如何工作的,嗯,复制赋值操作符在临时变量上调用,操作符复制右手参数,改变临时变量的状态。在语句之后,临时对象被丢弃。没有UB,只是无意义的复制。

可以通过使用如下引用限定符声明运算符来防止在右值上调用复制赋值Test& operator=(Test) & = default;:引用限定符仅在后来的 c++11 中添加,因此(隐式)复制分配不能被指定为更早的引用限定。据推测,C++11 没有更改隐式复制构造函数的限定符以防止破坏确实分配给右值的旧代码,即使这样的分配看起来确实毫无意义。高完整性 C++ 编码标准建议您将 ref 限定符与用户定义的复制赋值运算符一起使用。

于 2016-02-13T10:12:29.320 回答
1

就价值类别而言(至少对我而言),学习曲线非常陡峭,但我相信您的示例中的术语是正确的。所以

func()

确实返回一个纯右值并且来自 C++ 标准 par。3.10.5(我只有当前的草稿,你的段落编号可能会有所不同)我们阅读:

对象的左值是修改对象所必需的,但在某些情况下,类类型的右值也可用于修改其所指对象。[示例:为对象(9.3)调用的成员函数可以修改该对象。——结束示例]

因此,作为成员函数的赋值运算符,如标准中提到的示例中所示,是该规则的一个例外,允许修改右值。

这在编程界引起了很多批评,最极端的例子是C++ FQA摘录:

(是的,糖衣死亡陷阱,你们这些无能的啦啦队长X& obj=a.b().c()。——哎呀,b()是一个临时对象,c() 返回一个引用!不应该将它分配给一个引用。编译器警告的机会也不多。 )

但在真正的 C++ 编程中,它具有工业应用程序,例如命名参数idiom :

std::cout << X::create().setA(10).setB('Z') << std::endl;
于 2016-02-13T12:25:46.123 回答