Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why user-defined move-constructor disables the implicit copy-constructor?

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?

like image 909
amazingjxq Avatar asked Jun 29 '12 01:06

amazingjxq


People also ask

What does implicit move constructor do?

Implicitly-defined move constructor For non-union class types (class and struct), the move constructor performs full member-wise move of the object's bases and non-static members, in their initialization order, using direct initialization with an xvalue argument.

What is the difference between a move constructor and a copy constructor?

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.

Why does Noexcept move a constructor?

Tagging our move constructor with "noexcept" tells the compiler that it will not throw any exceptions. This condition is checked in C++ using the type trait function: "std::is_no_throw_move_constructible". This function will tell you whether the specifier is correctly set on your move constructor.

Is implicitly-declared as deleted?

The implicitly-declared or defaulted copy assignment operator for class T is defined as deleted in any of the following is true: T has a non-static data member that is const. T has a non-static data member of a reference type.


2 Answers

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.

like image 97
Howard Hinnant Avatar answered Sep 24 '22 08:09

Howard Hinnant


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.)

like image 22
ildjarn Avatar answered Sep 22 '22 08:09

ildjarn