为什么允许 T() = T()?
我相信表达式 T()
创建一个右值(根据标准)。但是,以下代码可以编译(至少在 gcc4.0 上):
class T {};
int main()
{
T() = T();
}
我知道从技术上讲这是可能的,因为可以在临时变量上调用成员函数,而上面的代码只是在从第一个 T 创建的右值临时变量上调用运算符= ()
。
但从概念上讲,这就像为右值分配一个新值。允许这样做有充分的理由吗?
编辑:我觉得这很奇怪的原因是它在内置类型上是严格禁止的,但在用户定义类型上却是允许的。例如,int(2) = int(3)
将无法编译,因为这是“赋值中的无效左值”。
所以我想真正的问题是,这种不一致的行为是否出于某种原因内置于语言中?还是出于某种历史原因? (例如,从概念上来说,只允许在右值表达式上调用 const 成员函数会更合理,但这是不可能的,因为这可能会破坏某些现有代码。)
I believe the expression T()
creates an rvalue (by the Standard). However, the following code compiles (at least on gcc4.0):
class T {};
int main()
{
T() = T();
}
I know technically this is possible because member functions can be invoked on temporaries and the above is just invoking the operator= on the rvalue temporary created from the first T()
.
But conceptually this is like assigning a new value to an rvalue. Is there a good reason why this is allowed?
Edit: The reason I find this odd is it's strictly forbidden on built-in types yet allowed on user-defined types. For example, int(2) = int(3)
won't compile because that is an "invalid lvalue in assignment".
So I guess the real question is, was this somewhat inconsistent behavior built into the language for a reason? Or is it there for some historical reason? (E.g it would be conceptually more sound to allow only const member functions to be invoked on rvalue expressions, but that cannot be done because that might break some existing code.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
允许这样做纯粹是因为运算符重载,并且您可能会重载运算符 = 来执行更奇特的操作,例如打印到控制台,或锁定互斥体,或任何其他真正的事情。
This is allowed purely because of operator overloading, and the possibility that you may overloaded the
operator =
to do something more fancy, like print to the console, or lock a mutex, or anything really.是的,您正在为右值分配一个新值。更准确地说,您正在对右值调用
operator =
成员函数。既然您没有使用内置赋值运算符,为什么您认为这应该是一个问题?operator =
是类的成员函数,它在大多数方面与类的任何其他成员函数类似,包括它可以在右值上调用这一事实。您可能还应该考虑这样一个事实:“作为右值”是表达式的属性,而不是对象的属性。
T()
表达式的计算结果确实是右值。尽管如此,T()
表达式生成的临时对象仍然是一个对象,它也可以作为左值访问。例如,可以对赋值的结果调用其他一些成员函数,它会通过*this
lvalue看到临时对象的“新”(新分配的)值还可以通过附加 const 引用来延长临时变量的生命周期正如 Johannes 在他的评论中正确指出的那样,这不会将其附加到临时对象上。const T& r = T() = T();
并且通过r
看到的值将是对象的“新”值。Yes, you are assigning a new value to an rvalue. More precisely, you are calling the
operator =
member function on a rvalue. Since you are not using the built-in assignment operator, why do you think this should be a problem?operator =
is a member function of the class, which in most respects is similar to any other member function of the class, including the fact that it can be called on rvalues.You should probably also take into account the fact that "being an rvalue" is a property of an expression, not a property of an object. It is true that
T()
expression evaluates to an rvalue. Nevertheless, the temporary object theT()
expression produces is still an object, which can be accessed as an lvalue as well. For example, some other member function can be called on the result of the assignment, and it will see the "new" (freshly assigned) value of the temporary object through*this
lvalueYou can also extend the lifetime of the temporary by attaching a const-reference to itAs Johannes correctly noted in his comment, this will not attach it to a temporary.const T& r = T() = T();
and the value seen throughr
will be the "new" value of the object.您可以限制 operator= 仅适用于 C++0x 中的左值:
You can restrict operator= to work only on lvalues in C++0x:
这就是为什么可以实现标准库中的几个类。例如,考虑一下
std::bitset<>::operator[]
如果您执行
bits[i] = true
,则您可以准确地将某个值分配给类类型的右值。operator[]
返回的代理可以访问空间有效地打包成整数的位。This is why several classes in the Standard library can be implemented. Consider for example
std::bitset<>::operator[]
If you do
bits[i] = true
you exactly assign some value to an rvalue of class type. The proxy that's returned byoperator[]
can access the bits which are space efficiently packed into integers.从一个角度来看,它是不一致的,但您忽略了它是如何一致的:1) 整数和其他内置类型的行为仍然与 C 中的行为相同,2) 类类型上的运算符=其行为与任何其他方法一样,不需要其他特殊情况。
自 C++ 诞生以来,C 兼容性就受到高度重视,如果没有它,C++ 可能不会有今天。所以这部分通常是一件好事。
第二点是轻描淡写的。不是特殊的大小写运算符 = 允许无意义代码“工作”,但为什么我们首先关心无意义代码呢?垃圾进来,垃圾出去。据我所知,目前的规则给了它一个明确的含义(UB在这里会很糟糕),而成本可以忽略不计。
鉴于我的建议,事情会进一步简化,因此
int() = int()
将被允许。 C++0x 开始通过右值引用、纯右值等朝这个方向发展。From one POV, it is inconsistent, but you're overlooking how it is consistent: 1) ints and other built-in types still behave as they do in C, 2) operator= on class-types behaves as any other method does without requiring yet another special case.
C compatibility has been highly valued since the beginning of C++, and C++ arguably wouldn't be here today without it. So that part's generally a Good Thing.
The second point is understated. Not special casing operator= allows nonsense code to "work", but why do we care about nonsense code in the first place? Garbage in, garbage out. The current rules give it a defined meaning (UB here would be bad) with negligible cost, as far as I've ever seen.
Given my druthers, things would be simplified even further, so
int() = int()
would be allowed. C++0x starts to head in that direction with rvalue-references, prvalues, etc.