According to the standard,
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator, and
— X does not have a user-declared destructor.
Now the following fails to compile
# include <utility> class Foo { public: Foo() = default; Foo(Foo const &) = delete; }; int main() { Foo f; Foo g(std::move(f)); // compilation fails here return 0; }
So it seems that a deleted function is considered as user-defined, which makes sense (it is not its default implementation). However, in that particular case, how would deleted copy construtor/assignment mess default move constructor/assignment?
I think this question has practical importance because manual generation and esp. maintenance of such default functions is error prone, while at the same time, the (righteous) increase of the use of classes such as std::unique_ptr
as class members made non-copyable classes much more common beasts than they used to be.
If any constructor is being called, it means a new object is being created in memory. So, the only difference between a copy constructor and a move constructor is whether the source object that is passed to the constructor will have its member fields copied or moved into the new object.
A move constructor of class T is a non-template constructor whose first parameter is T&&, const T&&, volatile T&&, or const volatile T&&, and either there are no other parameters, or the rest of the parameters all have default values.
Unlike the default constructor, the body of the copy constructor created by the compiler is not empty, it copies all data members of the passed object to the object which is being created.
The implicitly-declared or defaulted default constructor for class T is undefined (until C++11)defined as deleted (since C++11) if any of the following is true: T has a member of reference type without a brace-or-equal initializer.
user-declared
means either either user-provided (defined by the user), explicitly defaulted (= default
) or explicitly deleted (= delete
) in contrast with implicitly defaulted / deleted (such as your move constructor).
So in your case, yes the move constructor is implicitly deleted because the copy-constructor is explicitly deleted (and thus user-declared).
However, in that particular case, how would deleted copy constructor/assignment mess default move constructor/assignment?
It would not, but the standard does not make the difference between this case and a complicated one.
The shortest answer is that having an implicitly defined move-constructor with an explicitly deleted copy-constructor might be dangerous in some cases, the same when you have a user-defined destructor and no user-defined copy-constructor (see rule of three/five/zero). Now, you can argue that a user-defined destructor does not delete the copy-constructor, but this is simply a flaw in the language which cannot be removed because it would break a lot of old (bad) program. To quote Bjarne Stroustrup:
In an ideal world, I think we would decide on “no generation” as the default and provide a really simple notation for “give me all the usual operations.” [...] Also, a “no default operations” policy leads to compile time errors (which we should have an easy way to fix), whereas a generate operations by default policy leads to problems that cannot be detected until run time.
You can read more about this in N3174=10-0164.
Note that most people follow the rule of three/five/zero, and in my opinion you should. By implicitly deleting the default move-constructor, the standard is "protecting" you from mistakes and should have protected you a long time before by deleting copy-constructor in some cases (see Bjarne's paper).
Further reading if you are interested:
I think this question has practical importance because manual generation and esp. maintenance of such default functions is error prone, while at the same time, the (righteous) increase of the use of classes such as
std::unique_ptr
as class members made non-copyable classes much more common beasts than they used to be.
Marking the move constructor as explicitly defaulted will solve this problem:
class Foo { public: Foo() = default; Foo(Foo const &) = delete; Foo(Foo&&) = default; };
You get a non-copyable object with a default move constructor, and in my opinion these explicit declarations are better than implicit ones (e.g. by only declaring the move constructor as default
without deleting the copy-constructor).
As you stated, from §12.8
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
X does not have a user-declared copy constructor,
[...]
Note the user-declared. But if you look at §8.4.3:
A function definition of the form:
attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = delete ;
is called a deleted definition. A function with a deleted definition is also called a deleted function.
A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.
So the standard defines delete
d functions as user-declared (as you can see from above), even though they are delete
d and can't be used.
Then, according to §12.8, the implicit move constructor is not defined, because of the user-declared (with = delete;
) copy constructor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With