Re: auto-generated move assignment and base or member with a
by-value assignment
On Monday, February 24, 2014 10:38:12 PM UTC+1, Daniel Kr?gler wrote:
Am 24.02.2014 15:23, schrieb Krzysztof Czainski:
On Monday, February 17, 2014 9:35:29 PM UTC+1, Daniel Krugler wrote:
Krzysztof Czainski:
So the standard treats A's assignment operator taking A by value as
copy-assignment.
Yes, this needs to be the case, because a operator= overload of the
form
T& T::operator=(T)
was already a copy-assignment operator in previous versions of C++.
But there were no move assignment operators, nor move constructors in
previous C++.
[...]
I intend it to be a universal assignment operator. Typically:
T() = default;
T( T const& ) = default;
T( T&& ) = default;
T& T::operator=( T x ) { swap(*this,x); return *this; }
Here, I see T& T::operator=( T ) as a replacement for:
T& T::operator=( T const& )
T& T::operator=( T&& )
Notice, that the following sets of overloads are ambiguous:
T& T::operator=( T )
T& T::operator=( T&& )
or
T& T::operator=( T const& )
T& T::operator=( T )
Therefore I believe
T& T::operator=( T )
is both the copy and move assignment operator.
Sure, it is possible to consider it conceptually this way, but
technically it is a copy assignment operator (as it had been in C++03)
and this difference becomes obvious, if you consider the effects on
"container" types - that is types, that either contain T as non-static
data member or as a direct base class: Because T& T::operator=(T)
declares a copy-assignment operator, the corresponding "container" type
will (implicitly) always declare a copy-assignment operator as well.
Yes, so with T& T::operator=( T ) I think both: copy and move asignments
in "container" classes should be generated. And with the fix by CWG 1402
they will be.
But then, calling:
T& T::operator=( T )
"copy assignment" is misleading to me, because it seems no more a copy
assignment than a move assignment.
I would argue that describing this function as copy assignment character
of T makes sense, because you can apply an lvalue of T as argument, but
for any move-assignment operator this would not be possible. And if the
argument is an lvalue, this function will never modify it, so it truly
is non-mutating for lvalues. But I understand the point that you are
trying to make: For non-constant rvalues this function behaves
potentially mutating and has therefore some aspects of a typical move
operation.
HTH & Greetings from Bremen,
- Daniel Kr??gler
Thank you for explaining this, Daniel, but this did not convince me.
I will try to support my statement, that calling T& T::operator=( T ) is
misleading. Consider these classes:
struct A // noncopyable
{
A() = default;
A( A const& ) = delete;
A( A&& ) = delete; // this is redundant, here, isn't it?
A& operator=( A ) {}
};
An object of type A can't be copy assigned to or move assigned to,
and yet in the same time it has a public "copy assignment operator".
Come on, in practice A isn't copy assignable, nor move assignable!
struct B // movable only
{
B() = default;
B( B const& ) = delete;
B( B&& ) = default;
B& operator=( B ) {}
};
An object of type B can't be copy assigned to, but can be move assigned to,
and yet in the same time it has a public "copy assignment operator".
Come on, in practice B isn't copy assignable, but it is move assignable!
struct C // copyable only
{
C() = default;
C( C const& ) = default;
// C( C&& ) not declared, so that the copy ctor is used instead
C& operator=( C ) {}
};
An object of type C can be copy assigned to and move assigned to,
and both will do the same -- copy. This is the case, where it in deed
it has a copy assignment operator.
struct D // copyable and movable
{
D() = default;
D( D const& ) = default;
D( C&& ) = default;
D& operator=( D ) {}
};
An object of type D can be copy assigned to and truly move assigned to.
I mean truly move assigned, no copy occurs. But it has a public
"copy assignment operator". Come on, in practice D is both copy assignable
and move assignable.
I wonder, what should the traits is_copy_assignable and is_move_assignable
report for the above four classes?
As shown above, a class that has a "copy assignment operator" in practice
behaves like it had other set of assignment operators (except for class C).
I call that misleading. I think it should be clearly stated, that
any class that has T& operator=( T ) has:
- a copy assignment operator iff it is copy constructable, and
- a move assignment operator iff it is move constructable.
(iff == if and only if)
I hope this shows my concern better.
Regards,
Kris
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]