问题
I recently read (and unfortunately forgot where), that the best way to write operator= is like this:
foo &operator=(foo other)
{
swap(*this, other);
return *this;
}
instead of this:
foo &operator=(const foo &other)
{
foo copy(other);
swap(*this, copy);
return *this;
}
The idea is that if operator= is called with an rvalue, the first version can optimize away construction of a copy. So when called with a rvalue, the first version is faster and when called with an lvalue the two are equivalent.
I'm curious as to what other people think about this? Would people avoid the first version because of lack of explicitness? Am I correct that the first version can be better and can never be worse?
回答1:
You probably read it from: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
I don't have much to say since I think the link explains the rationale pretty well. Anecdotally I can confirm that the first form results in fewer copies in my builds with MSVC, which makes sense since compilers might not be able to do copy-elision on the second form. I agree that the first form is a strict improvement and is never worse than the second.
Edit: The first form might be a bit less idiomatic, but I don't think it's much less clear. (IMO, it's not any more surprising than seeing the copy-and-swap implementation of the assignment operator for the first time.)
Edit #2: Oops, I meant to write copy-elision, not RVO.
回答2:
I generally prefer the second one from readability and 'least surprise' point of view, however I do acknowledge that the first one can be more efficient when the parameter is a temporary.
The first one really can lead to no copies, not just the single copy and it's conceivable that this may be a genuine concern in extreme situations.
E.g. Take this test program. gcc -O3 -S
(gcc version 4.4.2 20091222 (Red Hat 4.4.2-20) (GCC)) generates one call to B's copy constructor but no calls to A's copy constructor for the function f
(the assignment operator is inlined for both A
and B
). A
and B
can both be taken to be very basic string classes. Allocation and copying for data
would occur in the constructors and deallocation in the destructor.
#include <algorithm>
class A
{
public:
explicit A(const char*);
A& operator=(A val) { swap(val); return *this; }
void swap(A& other) { std::swap(data, other.data); }
A(const A&);
~A();
private:
const char* data;
};
class B
{
public:
explicit B(const char*);
B& operator=(const B& val) { B tmp(val); swap(tmp); return *this; }
void swap(B& other) { std::swap(data, other.data); }
B(const B&);
~B();
private:
const char* data;
};
void f(A& a, B& b)
{
a = A("Hello");
b = B("World");
}
回答3:
Given this
foo &foo::operator=(foo other) {/*...*/ return *this;}
foo f();
in code like this
foo bar;
bar = f();
it might be easier for a compiler to eliminate the call to the copy constructor. With RVO, it could use the address of the operator's other
parameter as the place for f()
to construct its return value at.
It seems that this optimization is also possible for the second case, although I believe it might be harder. (Especially when the operator isn't inlined.)
回答4:
These two are actually the same. The only difference is where you press "Step In" in a debugger. And you should know in advance where to do it.
回答5:
I think that you might be confusing the difference between:
foo &operator=(const foo &other);
and const foo &operator=(const foo &other);
The first form should be used to allow for: (a = b) = c;
来源:https://stackoverflow.com/questions/2034635/explicit-copy-constructor-or-implicit-parameter-by-value