Re: Copy-assignment and C++0x move-assignment operator ambiguous?
class foo {
public:
foo & operator=(foo); // Argument passed "by value".
foo & operator=(foo &&); // C++0x rvalue reference.
};
Howard Hinnant wrote:
Yes, these two signatures are ambiguous. But it really is ok that
they are. If you wanted to treat lvalues and rvalues in different
ways you could:
foo&
foo::operator=(const foo& f)
{
foo tmp(f);
swap(tmp, *this);
return *this;
}
foo&
foo::operator=(foo&& f)
{
swap(f, *this);
return *this;
}
But with the by-value version:
foo&
foo::operator=(foo f)
{
swap(f, *this);
return *this;
}
you effectively achieve the same thing as the two-overload-reference
version. If an lvalue binds to f, it is copied in, and then you swap
with the copy. If an rvalue binds, the copy is typically
(universally?) elided, and you swap with the rvalue. Thus if you use
the by-value version, you don't have motivation to overload it with an
rvalue-ref version. There is no efficiency gain.
Cool! I just hope that for the by-value version, the copy is indeed
universally elided... Does anybody know of compilers that don't do copy
elision in this case?
Anyway, foo's assignment operator might still not properly support
moving by std::move, e.g., if it contains a shared_ptr<ostream>,
according to the LWG issue you submitted: #675, "Move assignment of
containers", www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#675
So the assignment operator might still need to be updated when upgrading
to C++0x, by clearing its contents before doing the swap:
foo&
foo::operator=(foo f)
{
foo().swap(*this); // Clear this!
f.swap(*this);
return *this;
}
Or by clearing its argument afterwards, if preferable (?):
foo&
foo::operator=(foo f)
{
f.swap(*this);
foo().swap(f); // Clear the argument!
return *this;
}
FWIW, I personally prefer to use the swap /member/ function when
implementing such an assignment, because it will trigger a compile error
when the class accidentally doesn't have one. OTOH, when calling the
free swap function while it isn't yet provided, the compiler might in
some cases select the generic std::swap, which would recursively call
the assignment, getting into an infinite loop. I'd rather have the
compile error :-)
Disclaimer for others: Not every class benefits from this style of
assignment operator. [...]
Sure. Thanks! Interestingly, if you have the copy-and-swap based
assigment overloaded for const reference and rvalue-reference, you can
still add a self-assigment test. Which isn't possible when having only
one assigment operator, having its argument by-value. Of course, it's
another discussion whether or not self-assignment should be a nothrow
operation...
When the assigment is overloaded for const-ref and rvalue-ref, the one
that has const-ref argument doesn't need to clear. So I guess there
/might/ still be some reasons for people to consider rewriting their
copy-and-swap assignments for C++0x...
Kind regards, Niels
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]