For a class A
, can we use
A& operator=( A other ) { /* ... */ }
instead of
A& operator=( const A& other ) { /* ... */ }
A& operator=( const A&& other ) { /* ... */ }
without worsening performance or other negative effects?
For a class A
, can we use
A& operator=( A other ) { /* ... */ }
instead of
A& operator=( const A& other ) { /* ... */ }
A& operator=( const A&& other ) { /* ... */ }
without worsening performance or other negative effects?
Actually, you can implement copy-and-swap with
A& operator=( A other ) { swap(other, *this); return *this; }
for both copy and move assignment but the unconditional copy for self-assignment operator will downgrade performance.
Except, above function can't be marked move should be lightweight, cheap, it isn't required but it should be lightweight. Hence, move constructor/assignment should be noexcept
,noexcept
. without noexcept
move constructor/assignment operator, move_if_noexcept
can't be called when the container grows, the container will go back to copy.
Hence, the recommended way is:
A& operator=( const A& other ) {
if (std::addressof(other) != this) {
A(other).swap(*this); // Copy constructor may throw
}
return *this;
}
A& operator=( A&& other ) noexcept { // note no const here
// we don't check self assignment because
// an object binds to an rvalue reference it is one of two things:
// - A temporary.
// - An object the caller wants you to believe is a temporary.
swap(other);
return *this;
}
without worsening performance or other negative effects?
It depends on the data members and base classes of A
, and can even depend on the bases and members of A's bases and members.
Here is a link to a part of a video that demonstrates that if A
has a vector
as a data member, then the copy/swap idiom can cost a 70% performance hit for the copy assignment operator on average. This is compared to the much easier approach of defaulting the assignment special members.
http://www.youtube.com/watch?v=vLinb2fgkHk&t=35m30s
class A
{
std::vector<int> v_;
public:
// ...
};
You can expect string
data members to have the same impact as vector
data members.
If you aren't sure for your A
, and if performance is important to you, try it both ways and measure. This is precisely what is demonstrated in the video.
Performance of "move" contexts might suffer slightly.
If your class A
is movable, then in moving contexts the pass-by-value copy-and-swap implementation will implement move as a sequence of two operations:
other
by using A
's move constructor.other
to *this
(or swap other
with *this
).The second implementation (with dedicated move-assignment) can do the same thing in just one operation: it will move (or swap) the right-hand side value with *this
directly.
So, potentially, the copy-and-swap variant involves the overhead of one extra move.
Performance of "copy" contexts can suffer from slightly to severely.
If your left-hand side (LHS) has some copyable internal resource (like a memory block) that can accommodate the corresponding RHS value without reallocation, then a dedicated implementation of copy-assignment just needs to copy the data from RHS resource to LHS resource. No memory allocation is necessary.
In copy-and-swap variant the copy is created unconditionally and the aforementioned internal resource has to be allocated and deallocated unconditionally. This can have a major negative performance impact.