Why user-defined move-constructor disables the implicit copy-constructor?
Solution 1
I've upvoted ildjarn's answer because I found it both accurate and humorous. :-)
I'm providing an alternate answer because I'm assuming because of the title of the question that the OP might want to know why the standard says so.
background
C++ has implicitly generated copy members because if it didn't, it would've been still-born in 1985 because it was so incompatible with C. And in that case we wouldn't be having this conversation today because C++ wouldn't exist.
That being said, implicitly generated copy members are akin to a "deal with the devil". C++ couldn't have been born without them. But they are evil in that they silently generate incorrect code in a significant number of instances. The C++ committee isn't stupid, they know this.
C++11
Now that C++ has been born, and has evolved into a successful grownup, the committee would just love to say: we're not doing implicitly generated copy members any more. They are too dangerous. If you want an implicitly generated copy member you have to opt-in to that decision (as opposed to opt-out of it). However considering the amount of existing C++ code that would break if this was done, that would be tantamount to suicide. There is a huge backwards compatibility concern that is quite justified.
So the committee reached a compromise position: If you declare move members (which legacy C++ code can't do), then we're going to assume that the default copy members are likely to do the wrong thing. Opt-in (with =default
) if you want them. Or write them yourself. Otherwise they are implicitly deleted. Our experience to-date in a world with move-only types indicates that this default position is actually quite commonly what is desired (e.g. unique_ptr
, ofstream
, future
, etc.). And the expense of opting-in is actually quite small with = default
.
Looking Forward
The committee would love to even say: If you've written a destructor, it is likely that the implicit copy members are incorrect, so we will delete them. This is the C++98/03 "rule of three". However even that would break lots of code. However the committee has said in C++11 that if you provide a user-declared destructor, the implicit generation of copy members is deprecated. That means that this feature could be removed in a future standard. And that any day now your compiler might start issuing "deprecated warnings" in this situation (the standard can not specify warnings).
Conclusion
So be forewarned: C++ has grown up and matured over the decades. And that means that your father's C++ may need migrating to deal with your child's C++. It is a slow, gradual process so that you don't throw up your hands and just port to another language. But it is change, even if slow.
Solution 2
Because the C++ standard says so – §12.8/7:
If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted. The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. Thus, for the class definition
struct X { X(const X&, int); };
a copy constructor is implicitly-declared. If the user-declared constructor is later defined as
X::X(const X& x, int i =0) { /* ... */ }
then any use of X’s copy constructor is ill-formed because of the ambiguity; no diagnostic is required.
(Emphasis mine.)
Related videos on Youtube
amazingjxq
Updated on December 13, 2020Comments
-
amazingjxq over 3 years
While I'm reading boost/shared_ptr.hpp, i saw this code:
// generated copy constructor, destructor are fine... #if defined( BOOST_HAS_RVALUE_REFS ) // ... except in C++0x, move disables the implicit copy shared_ptr( shared_ptr const & r ): px( r.px ), pn( r.pn ) // never throws { } #endif
What does the comment "generated copy constructor, destructor are fine except in C++11, move disables the implicit copy" mean here? Shall we always write the copy ctor ourselves to prevent this situation in C++11?
-
bames53 almost 12 years"any day now your compiler might start issue "deprecated warnings" in this situation" sounds like another good addition for -Wdeprecated. And a warning on uses of _dynamic-exception-specification_s as well.
-
Nawaz almost 12 yearsno diagnostic is required.? What does it mean? No error/warning will be issued for the ambiguity?
-
ildjarn almost 12 years@Nawaz : One can be issued, but is not required to be issued, since it's only deprecated at this point.
-
Nawaz almost 12 years+1. Awesome explanation. Quite sensible decision taken by the committe.
-
Aaron S over 6 yearsGreat explanation on the why part of the question.