简体   繁体   English

将从函数返回的Rvalue分配给另一个Rvalue

[英]Assigning Rvalue returned from function to another Rvalue

class Test {

public:

    int n1;

};

Test func() {

    return Test();

}

int main() {

    func() = Test();

}

This doesn't make sense to me. 这对我来说没有意义。 How and why is this allowed? 如何以及为什么允许这样做? Is it undefined behavior? 是不确定的行为? If a function returns an rvalue, then how is it possible to set an rvalue to another rvalue? 如果函数返回一个rvalue,那么如何将rvalue设置为另一个rvalue呢? If I tried this with any primitive types, it would give me an error like I expect. 如果我尝试使用任何原始类型,它会给我一个像我期望的错误。

I know that lvalues are a place in memory, so is the function creating a temporary lvalue (rvalue?) and assigning it to another lvalue? 我知道左值是内存中的一个位置,创建临时左值(rvalue?)并将其赋值给另一个左值的函数也是如此? Can someone explain how this syntax works? 有人可以解释这种语法的工作原理吗

The value category of the function call expression is in fact an rvalue. 函数调用表达式的值类别实际上是rvalue。

Indeed, you may not call the copy assignment operator on rvalue primitives. 实际上,您可能无法在rvalue基元上调用复制赋值运算符。 The historical definition of rvalues in C was in fact the distinction that they may not be on the left side of an assigment. C中rvalues的历史定义实际上是它们可能不在分类左侧的区别。

The assignment operators of classes are a bit different, though. 但是,类的赋值运算符有点不同。 They're regular member functions. 他们是正式的会员职能。 And no rule prevents calling member functions of rvalues. 没有规则可以阻止调用rvalues的成员函数。 Indeed, it's often quite useful when the functions have side-effects. 实际上,当函数具有副作用时,它通常非常有用。

How it works, well, the copy assignment operator is called on the temporary, the operator copies the right hand argument, changing the state of the temporary. 它是如何工作的,好吧,临时调用复制赋值运算符,运算符复制右手参数,改变临时状态。 After the statement, the temporary object is discarded. 在语句之后,临时对象被丢弃。 There is no UB, just pointless copying. 没有UB,只是毫无意义的复制。

You can prevent calling copy assignment on an rvalue, by declaring the operator with a reference qualifier like this: Test& operator=(Test) & = default; 可以通过使用如下参考限定符声明操作符阻止在右值上调用复制赋值: Test& operator=(Test) & = default; . Ref-qualifiers were added only later in c++11, so (implicit) copy assignment couldn't have been specified to be ref-qualified earlier. Ref-qualifiers仅在c ++ 11中稍后添加,因此(隐式)复制赋值不能指定为先前的ref-qualified。 Presumably, C++11 didn't change the qualifier of implicit copy constructor to prevent breaking old code that does assign to an rvalue, even though such assignment does seem quite pointless. 据推测,C ++ 11并没有改变隐式复制构造函数的限定符,以防止破坏分配给rvalue的旧代码,即使这样的赋值似乎毫无意义。 The High Integrity C++ Coding Standard recommends that you do use ref qualifier with user defined copy assignment operators. 高完整性C ++编码标准建议您将ref限定符与用户定义的副本赋值运算符一起使用。

There's a pretty steep learning curve as far as value categories are concerned (at least to me), but I believe you have got the terminology right on your example. 价值类别而言(至少对我来说),学习曲线非常陡峭,但我相信你的例子就是正确的术语 So 所以

func()

indeed returns a prvalue and from the C++ Standard par. 确实返回一个prvalue和C ++标准par。 3.10.5 (I only have the current draft , your paragraph number may vary) we read : 3.10.5 (我只有当前的草案 ,你的段落编号可能有所不同)我们读到:

An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances . 为了修改对象,对象的左值是必要的,除了在某些情况下也可以使用类类型的右值来修改它的指示对象 [Example: a member function called for an object (9.3) can modify the object. [示例:调用对象(9.3)的成员函数可以修改对象。 — end example] - 结束例子]

So the assignment operator, which is a member function, as in the example mentioned in the standard is an exception to the rule, allowing rvalues to be modified. 因此,作为成员函数的赋值运算符(如标准中提到的示例)是规则的例外,允许修改rvalues。

This has spawned much criticism in the programming world, with its most extreme example this C++ FQA excerpt : 这在编程世界引起了很多批评,其最极端的例子就是这个C ++ FQA摘录:

(Yes, sugar-coated death traps, you clueless cheerleaders. X& obj=ab().c() – oops, b() is a temporary object and c() returns a reference into it! Shouldn't have assigned that to a reference. Not many chances for a compiler warning, either.) (是的,糖衣死亡陷阱,你无能为力的拉拉队X& obj=ab().c() - oops, b()是一个临时对象,c()返回一个引用!不应该分配给它参考。编译器警告的机会也不多。)

But in real C++ programming it has industrial applications like the named parameter idiom : 但在实际的C ++编程中,它有工业应用程序,命名参数 idiom:

std::cout << X::create().setA(10).setB('Z') << std::endl;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM