class Test {
public:
int n1;
};
Test func() {
return Test();
}
int main() {
func() = Test();
}
这对我没有意义。如何以及为什么允许这样做?是不确定的行为?如果函数返回一个rvalue,那么如何将rvalue设置为另一个rvalue呢?如果我用任何原始类型尝试这个,它会给我一个像我期望的错误。
我知道左值是内存中的一个位置,创建临时左值(rvalue?)并将其赋值给另一个左值的函数也是如此?有人可以解释这种语法是如何工作的吗?
答案 0 :(得分:7)
函数调用表达式的值类别实际上是一个右值。
实际上,您可能无法在rvalue基元上调用复制赋值运算符。 C中rvalues的历史定义实际上是它们可能不在分类左侧的区别。
但是类的赋值运算符有点不同。他们是正式的会员职能。没有规则可以阻止调用rvalues的成员函数。实际上,当函数有副作用时,它通常非常有用。
如何工作,好吧,临时调用复制赋值运算符,运算符复制右手参数,改变临时状态。在语句之后,临时对象被丢弃。没有UB,只是毫无意义的复制。
可以阻止在rvalue上调用副本分配,方法是使用这样的引用限定符声明运算符:#include<stdio.h>
void helper(char *i) {
printf("%c", *i);
*i = 't';
printf("%c", *i);
}
void main() {
char *g = "t";
helper(g);
printf("%c", *g);
。 Ref-qualifiers仅在c ++ 11中稍后添加,因此(隐式)复制赋值不能指定为先前的ref-qualified。据推测,C ++ 11并没有改变隐式复制构造函数的限定符,以防止破坏分配给rvalue的旧代码,即使这样的赋值似乎毫无意义。 建议您将ref限定符与用户定义的副本赋值运算符一起使用。
答案 1 :(得分:1)
就value categories而言(至少对我而言)有一个相当陡峭的学习曲线,但我相信你的榜样上有terminology。所以
func()
确实返回 prvalue 并从C ++ Standard par返回。 3.10.5 (我只有当前draft,您的段落编号可能会有所不同)我们读到:
对象的左值是必要的,以便修改对象,除了类类型的右值也可用于在某些情况下修改其指示物。 [例子:一个成员函数调用了一个 object(9.3)可以修改对象。 - 结束例子]
因此,作为成员函数的赋值运算符(如标准中提到的示例)是规则的例外,允许修改rvalues。
这引起了编程界的许多批评,最极端的例子就是C++ FQA摘录:
(是的,糖衣死亡陷阱,你是无能为力的啦啦队。
X& obj=a.b().c()
- oops,b()
是一个临时对象,c()返回一个引用!不应该分配给它引用。编译器警告的机会也不多。)
但在真正的C ++编程中,它industrial applications就像named parameter成语一样:
std::cout << X::create().setA(10).setB('Z') << std::endl;