将从函数返回的右值分配给另一个右值
Assigning Rvalue returned from function to another Rvalue
class Test {
public:
int n1;
};
Test func() {
return Test();
}
int main() {
func() = Test();
}
这对我来说没有意义。如何以及为什么允许这样做?它是未定义的行为吗?如果函数 returns 是一个右值,那么如何将一个右值设置为另一个右值?如果我用任何原始类型尝试这个,它会像我预期的那样给我一个错误。
我知道左值是内存中的一个位置,那么函数是否创建一个临时左值(右值?)并将其分配给另一个左值?谁能解释一下这个语法是如何工作的?
函数调用表达式的值类别实际上是一个右值。
确实,您不能对右值基元调用复制赋值运算符。 C 中右值的历史定义实际上是它们不能位于赋值左侧的区别。
不过,类 的赋值运算符有点不同。它们是常规成员函数。并且没有规则阻止调用右值的成员函数。事实上,当函数有副作用时,它通常非常有用。
它是如何工作的,嗯,在临时对象上调用复制赋值运算符,运算符复制右边的参数,改变临时对象的状态。语句之后,临时对象被丢弃。没有UB,只是无意义的复制。
您 可以 阻止对右值调用复制赋值,方法是使用这样的引用限定符声明运算符:Test& operator=(Test) & = default;
。引用限定符只是在 c++11 的后期添加的,因此(隐式)复制赋值不能更早地指定为引用限定符。据推测,C++11 没有更改隐式复制构造函数的限定符以防止破坏确实分配给右值的旧代码,即使这样的分配看起来毫无意义。 High Integrity C++ Coding Standard 建议您将 ref 限定符与用户定义的复制赋值运算符一起使用。
就您的示例而言,就 value categories are concerned (at least to me), but I believe you have got the terminology 而言,学习曲线非常陡峭。所以
func()
确实returns一个prvalue和C++标准相提并论。 3.10.5(我只有当前的draft,你的段落编号可能会有所不同)我们读到:
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. — end example]
因此,赋值运算符是一个成员函数,如标准中提到的示例中所示,是该规则的一个例外,允许修改右值。
这在编程界引起了很多批评,最极端的例子是 C++ FQA 摘录:
(Yes, sugar-coated death traps, you clueless cheerleaders. X& obj=a.b().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.)
但在真正的 C++ 编程中它有 industrial applications like the named parameter 习语:
std::cout << X::create().setA(10).setB('Z') << std::endl;
class Test {
public:
int n1;
};
Test func() {
return Test();
}
int main() {
func() = Test();
}
这对我来说没有意义。如何以及为什么允许这样做?它是未定义的行为吗?如果函数 returns 是一个右值,那么如何将一个右值设置为另一个右值?如果我用任何原始类型尝试这个,它会像我预期的那样给我一个错误。
我知道左值是内存中的一个位置,那么函数是否创建一个临时左值(右值?)并将其分配给另一个左值?谁能解释一下这个语法是如何工作的?
函数调用表达式的值类别实际上是一个右值。
确实,您不能对右值基元调用复制赋值运算符。 C 中右值的历史定义实际上是它们不能位于赋值左侧的区别。
不过,类 的赋值运算符有点不同。它们是常规成员函数。并且没有规则阻止调用右值的成员函数。事实上,当函数有副作用时,它通常非常有用。
它是如何工作的,嗯,在临时对象上调用复制赋值运算符,运算符复制右边的参数,改变临时对象的状态。语句之后,临时对象被丢弃。没有UB,只是无意义的复制。
您 可以 阻止对右值调用复制赋值,方法是使用这样的引用限定符声明运算符:Test& operator=(Test) & = default;
。引用限定符只是在 c++11 的后期添加的,因此(隐式)复制赋值不能更早地指定为引用限定符。据推测,C++11 没有更改隐式复制构造函数的限定符以防止破坏确实分配给右值的旧代码,即使这样的分配看起来毫无意义。 High Integrity C++ Coding Standard 建议您将 ref 限定符与用户定义的复制赋值运算符一起使用。
就您的示例而言,就 value categories are concerned (at least to me), but I believe you have got the terminology 而言,学习曲线非常陡峭。所以
func()
确实returns一个prvalue和C++标准相提并论。 3.10.5(我只有当前的draft,你的段落编号可能会有所不同)我们读到:
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. — end example]
因此,赋值运算符是一个成员函数,如标准中提到的示例中所示,是该规则的一个例外,允许修改右值。
这在编程界引起了很多批评,最极端的例子是 C++ FQA 摘录:
(Yes, sugar-coated death traps, you clueless cheerleaders.
X& obj=a.b().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.)
但在真正的 C++ 编程中它有 industrial applications like the named parameter 习语:
std::cout << X::create().setA(10).setB('Z') << std::endl;