C++ 为什么复制赋值运算符必须返回引用/常量引用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3105798/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why must the copy assignment operator return a reference/const reference?
提问by bks
In C++, the concept of returning reference from the copy assignment operator is unclear to me. Why can't the copy assignment operator return a copy of the new object? In addition, if I have class A
, and the following:
在 C++ 中,我不清楚从复制赋值运算符返回引用的概念。为什么复制赋值运算符不能返回新对象的副本?此外,如果我有 class A
,以及以下内容:
A a1(param);
A a2 = a1;
A a3;
a3 = a2; //<--- this is the problematic line
The operator=
is defined as follows:
的operator=
定义如下:
A A::operator=(const A& a)
{
if (this == &a)
{
return *this;
}
param = a.param;
return *this;
}
回答by Michael Burr
Strictly speaking, the result of a copy assignment operator doesn't need to return a reference, though to mimic the default behavior the C++ compiler uses, it should return a non-const reference to the object that is assigned to (an implicitly generated copy assignment operator will return a non-const reference - C++03: 12.8/10). I've seen a fair bit of code that returns void
from copy assignment overloads, and I can't recall when that caused a serious problem. Returning void
will prevent users from 'assignment chaining' (a = b = c;
), and will prevent using the result of an assignment in a test expression, for example. While that kind of code is by no means unheard of, I also don't think it's particularly common - especially for non-primitive types (unless the interface for a class intends for these kinds of tests, such as for iostreams).
严格来说,复制赋值运算符的结果不需要返回引用,尽管为了模仿 C++ 编译器使用的默认行为,它应该返回对分配给的对象的非常量引用(隐式生成的副本赋值运算符将返回一个非常量引用 - C++03: 12.8/10)。我见过很多void
从复制赋值重载返回的代码,我不记得它何时导致严重问题。返回void
将阻止用户“分配链接”(a = b = c;
),并且会阻止在测试表达式中使用赋值的结果,例如。虽然这种代码绝非闻所未闻,但我也不认为它特别常见 - 特别是对于非原始类型(除非类的接口旨在用于此类测试,例如用于 iostreams)。
I'm not recommending that you do this, just pointing out that it's permitted and that it doesn't seem to cause a whole lot of problems.
我不建议您这样做,只是指出这是允许的,并且它似乎不会引起很多问题。
These other SO questions are related (probably not quite dupes) that have information/opinions that might be of interest to you.
这些其他 SO 问题是相关的(可能不是很受骗),其中包含您可能感兴趣的信息/意见。
回答by Alex Collins
A bit of clarification as to why it's preferable to return by reference for operator=
versus return by value --- as the chain a = b = c
will work fine if a value is returned.
关于为什么最好通过引用返回而operator=
不是通过值返回的原因进行了一些澄清——因为a = b = c
如果返回一个值,链将正常工作。
If you return a reference, minimal work is done. The values from one object are copied to another object.
如果您返回一个引用,则只需完成最少的工作。一个对象的值被复制到另一个对象。
However, if you return by value for operator=
, you will call a constructor AND destructor EACH time that the assignment operator is called!!
但是,如果您按值返回 for operator=
,您将在每次调用赋值运算符时调用构造函数和析构函数!!
So, given:
所以,给定:
A& operator=(const A& rhs) { /* ... */ };
Then,
然后,
a = b = c; // calls assignment operator above twice. Nice and simple.
But,
但,
A operator=(const A& rhs) { /* ... */ };
a = b = c; // calls assignment operator twice, calls copy constructor twice, calls destructor type to delete the temporary values! Very wasteful and nothing gained!
In sum, there is nothing gained by returning by value, but a lot to lose.
总而言之,按价值回归没有什么收获,反而失去了很多。
(Note: This isn't meant to address the advantages of having the assignment operator return an lvalue. Read the other posts for why that might be preferable)
(注意:这并不是为了解决让赋值运算符返回左值的优点。阅读其他帖子了解为什么这可能更可取)
回答by Jerry Coffin
When you overload operator=
, you canwrite it to return whatever type you want. If you want to badly enough, you can overload X::operator=
to return (for example) an instance of some completely different class Y
or Z
. This is generally highlyinadvisable though.
当您重载 时operator=
,您可以编写它以返回您想要的任何类型。如果您非常想要,您可以重载X::operator=
以返回(例如)某个完全不同的类Y
或Z
. 不过,这通常是非常不可取的。
In particular, you usually want to support chaining of operator=
just like C does. For example:
特别是,您通常希望operator=
像 C 一样支持链接。例如:
int x, y, z;
x = y = z = 0;
That being the case, you usually want to return an lvalue or rvalue of the type being assigned to. That only leaves the question of whether to return a reference to X, a const reference to X, or an X (by value).
在这种情况下,您通常希望返回所分配类型的左值或右值。那只剩下是返回对 X 的引用、对 X 的 const 引用还是返回 X(按值)的问题。
Returning a const reference to X is generally a poor idea. In particular, a const reference is allowed to bind to a temporary object. The lifetime of the temporary is extended to the lifetime of the reference to which it's bound--but not recursively to the lifetime of whatever that might be assigned to. This makes it easy to return a dangling reference--the const reference binds to a temporary object. That object's lifetime is extended to the lifetime of the reference (which ends at the end of the function). By the time the function returns, the lifetime of the reference and temporary have ended, so what's assigned is a dangling reference.
返回对 X 的 const 引用通常是一个糟糕的主意。特别是,const 引用被允许绑定到一个临时对象。临时对象的生命周期扩展到它所绑定的引用的生命周期——但不会递归地扩展到可能分配给的任何对象的生命周期。这使得返回悬空引用变得容易——const 引用绑定到一个临时对象。该对象的生命周期延长到引用的生命周期(在函数结束时结束)。到函数返回时,引用和临时引用的生命周期已经结束,因此分配的是悬空引用。
Of course, returning a non-const reference doesn't provide complete protection against this, but at least makes you work a little harder at it. You can still (for example) define some local, and return a reference to it (but most compilers can and will warn about this too).
当然,返回非常量引用并不能完全防止这种情况发生,但至少会让您在这方面更加努力。您仍然可以(例如)定义一些本地,并返回对它的引用(但大多数编译器也可以并且会对此发出警告)。
Returning a value instead of a reference has both theoretical and practical problems. On the theoretical side, you have a basic disconnect between =
normally means and what it means in this case. In particular, where assignment normally means "take this existing source and assign its value to this existing destination", it starts to mean something more like "take this existing source, create a copy of it, and assign that value to this existing destination."
返回值而不是引用具有理论和实际问题。从理论上讲,您在=
通常的意思和在这种情况下的意思之间有一个基本的脱节。特别是,赋值通常意味着“获取此现有源并将其值分配给此现有目标”,它开始意味着更像是“获取此现有源,创建它的副本,然后将该值分配给此现有目标。 ”
From a practical viewpoint, especially before rvalue references were invented, that could have a significant impact on performance--creating an entire new object in the course of copying A to B was unexpected and often quite slow. If, for example, I had a small vector, and assigned it to a larger vector, I'd expect that to take, at most, time to copy elements of the small vector plus a (little) fixed overhead to adjust the size of the destination vector. If that instead involved twocopies, one from source to temp, another from temp to destination, and (worse) a dynamic allocation for the temporary vector, my expectation about the complexity of the operation would be entirelydestroyed. For a small vector, the time for the dynamic allocation could easily be many times higher than the time to copy the elements.
从实际的角度来看,尤其是在发明右值引用之前,这可能会对性能产生重大影响——在将 A 复制到 B 的过程中创建一个全新的对象是出乎意料的,而且通常很慢。例如,如果我有一个小向量,并将它分配给一个更大的向量,我希望最多需要时间来复制小向量的元素加上一个(小)固定开销来调整大小目标向量。如果这涉及两个副本,一个从源到临时,另一个从临时到目标,以及(更糟糕的)临时向量的动态分配,我对操作复杂性的期望将完全是摧毁了。对于一个小向量,动态分配的时间很容易比复制元素的时间高很多倍。
The only other option (added in C++11) would be to return an rvalue reference. This could easily lead to unexpected results--a chained assignment like a=b=c;
could destroy the contents of b
and/or c
, which would be quite unexpected.
唯一的其他选项(在 C++11 中添加)是返回一个右值引用。这很容易导致意外的结果——像这样的链式赋值a=b=c;
可能会破坏b
and/or的内容c
,这是非常出乎意料的。
That leaves returning a normal reference (not a reference to const, nor an rvalue reference) as the only option that (reasonably) dependably produces what most people normally want.
这使得返回普通引用(不是对 const 的引用,也不是对右值的引用)作为唯一的选项(合理地)可靠地产生大多数人通常想要的东西。
回答by Puppy
It's partly because returning a reference to self is faster than returning by value, but in addition, it's to allow the original semantics that exist in primitive types.
部分原因是返回对 self 的引用比按值返回更快,但此外,它允许原始类型中存在的原始语义。
回答by MSN
operator=
can be defined to return whatever you want. You need to be more specific as to what the problem actually is; I suspect that you have the copy constructor use operator=
internally and that causes a stack overflow, as the copy constructor calls operator=
which must use the copy constructor to return A
by value ad infinitum.
operator=
可以定义为返回任何你想要的。您需要更具体地了解问题究竟是什么;我怀疑您在operator=
内部使用了复制构造函数并导致堆栈溢出,因为复制构造函数调用operator=
必须使用复制构造函数A
按值无限返回。
回答by Cheers and hth. - Alf
There is no core language requirement on the result type of a user-defined operator=
, but the standard library does have such a requirement:
用户定义的结果类型没有核心语言要求operator=
,但标准库确实有这样的要求:
C++98 §23.1/3:
C++98 §23.1/3:
”The type of objects stored in these components must meet the requirements of
CopyConstructible
types (20.1.3), and the additional requirements ofAssignable
types.
”这些组件中存储的对象类型必须满足
CopyConstructible
类型(20.1.3)的要求,以及Assignable
类型的附加要求。
C++98 §23.1/4:
C++98 §23.1/4:
”In Table 64,
T
is the type used to instantiate the container,t
is a value ofT
, andu
is a value of (possiblyconst
)T
.
”在表 64 中,
T
是用于实例化容器的类型,t
是 的值T
,u
是(可能const
)的值T
。
Returning a copy by value would still support assignment chaining like a = b = c = 42;
, because the assignment operator is right-associative, i.e. this is parsed as a = (b = (c = 42));
. But returning a copy would prohibit meaningless constructions like (a = b) = 666;
. For a small class returning a copy could conceivably be most efficient, while for a larger class returning by reference will generally be most efficient (and a copy, prohibitively inefficient).
按值返回副本仍然支持像 那样的赋值链a = b = c = 42;
,因为赋值运算符是右结合的,即 this 被解析为a = (b = (c = 42));
。但是返回副本将禁止像(a = b) = 666;
. 对于一个小的类来说,返回一个副本可能是最有效的,而对于一个更大的类来说,通过引用返回通常是最有效的(而一个副本,效率低得令人望而却步)。
Until I learned about the standard library requirement I used to let operator=
return void
, for efficiency and to avoid the absurdity of supporting side-effect based bad code.
在我了解标准库要求之前,我曾经让operator=
return void
,以提高效率并避免支持基于副作用的不良代码的荒谬性。
With C++11 there is additionally the requirement of T&
result type for default
-ing the assignment operator, because
在 C++11 中,对赋值运算符 -ing还需要T&
结果类型default
,因为
C++11 §8.4.2/1:
C++11 §8.4.2/1:
”A function that is explicitly defaulted shall […] have the same declared function type (except for possibly differing ref-qualifiersand except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const
T
”, whereT
is the name of the member function's class) as if it had been implicitly declared
”显式默认的函数应 [...] 具有相同的声明函数类型(除了可能不同的ref 限定符,以及在复制构造函数或复制赋值运算符的情况下,参数类型可能是“对非const
T
”,其中T
是成员函数类的名称),就好像它已被隐式声明一样