Make copy assignment non-`virtual`, take the parameter by `const&`, and return by non-`const&`
Reason
It is simple and efficient. If you want to optimize for rvalues, provide an overload that takes an && (see F.18).
Example
class Foo {
public:
Foo& operator=(const Foo& x)
{
// GOOD: no need to check for self-assignment (other than performance)
auto tmp = x;
swap(tmp); // see C.83
return *this;
}
// ...
};
Foo a;
Foo b;
Foo f();
a = b; // assign lvalue: copy
a = f(); // assign rvalue: potentially move
Note
The swap implementation technique offers the strong guarantee.
Example
But what if you can get significantly better performance by not making a temporary copy? Consider a simple Vector intended for a domain where assignment of large, equal-sized Vectors is common. In this case, the copy of elements implied by the swap implementation technique could cause an order of magnitude increase in cost:
template<typename T>
class Vector {
public:
Vector& operator=(const Vector&);
// ...
private:
T* elem;
int sz;
};
Vector& Vector::operator=(const Vector& a)
{
if (a.sz > sz) {
// ... use the swap technique, it can't be bettered ...
return *this;
}
// ... copy sz elements from *a.elem to elem ...
if (a.sz < sz) {
// ... destroy the surplus elements in *this and adjust size ...
}
return *this;
}
By writing directly to the target elements, we will get only the basic guarantee rather than the strong guarantee offered by the swap technique. Beware of self-assignment.
Alternatives: If you think you need a virtual assignment operator, and understand why that's deeply problematic, don't call it operator=. Make it a named function like virtual void assign(const Foo&). See copy constructor vs. clone().
Enforcement
Look at the destructor to determine if the type has pointer semantics or value semantics.
- (Simple) An assignment operator should not be virtual. Here be dragons!
- (Simple) An assignment operator should return
T&to enable chaining, not alternatives likeconst T&which interfere with composability and putting objects in containers. - (Moderate) An assignment operator should (implicitly or explicitly) invoke all base and member assignment operators.