35
votes

In C++98, the C++ compiler could automatically generate copy constructor and copy assignment operator via member-wise copy, e.g.

struct X {
    std::string s;
    std::vector<int> v;
    int n;
};

The compiler automatically generates copy constructor and copy assignment operator for X, using member-wise copy.

But how do things change in C++11 with move semantics?

Are the move constructor and move assignment operator automatically generated, like copy constructors and copy assignment operators?

Are there cases in which move operations are not automatically generated?

2
automatic moving is an optimization, and as such should ideally not affect any observable behavior other than speed. but e.g. a user defined destructor may rely on various assumptions about data members. scott meyers once started a long discussion about that in clc++ and subsequently in comp.std.c++, and the current rules (which I'm quite unclear on) reflect that discussion. - Cheers and hth. - Alf
If you like graphical, tabular results, slide 28 from github.com/HowardHinnant/presentations/blob/master/accu_2014/… (there have been reports that this does not display well with some pdf readers, if that is the case, try another one). - Howard Hinnant
@HowardHinnant: Your slides seem great. I will have a more detailed look. Thanks for sharing. - Mr.C64
@HowardHinnant: If you may want to write an answer with your slides linked, I'd be happy to upvote that. - Mr.C64
Visual Studio 2010-2013 does not generate them, period. From Support For C++11/14/17 Features on MSDN: "Rvalue references v3.0 adds new rules to automatically generate move constructors and move assignment operators under certain conditions. This is implemented in Visual Studio 2015". The "Rvalue references v3.0" they are talking about is N3053, Defining Move Special Member Functions (MARCH 2010). - jww

2 Answers

30
votes

From the standard Ch. 12 - Special member functions

Par 12.8 Copying and moving class objects (emphasis mine)

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

[ Note: When the move constructor is not implicitly declared or explicitly supplied, expressions that otherwise would have invoked the move constructor may instead invoke a copy constructor. —end note ]

Then 11 explains the rules for deleting the defaulted move constructor

11 . An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/ move constructor for a class X is defined as deleted (8.4.3) if X has:

— a variant member with a non-trivial corresponding constructor and X is a union-like class,

— a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,

— a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,

— any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor, or,

— for the copy constructor, a non-static data member of rvalue reference type. A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3, 13.4).

[ Note: A deleted move constructor would otherwise interfere with initialization from an rvalue which can use the copy constructor instead. —end note ]


On the complexity of it all *

The rules can be somewhat overwhelming. It's good to use some technique to bypass the complexity. Examples are :

  1. Make use of the rule of zero to simplify the writing of the majority of your classes.
  2. (On implicitly deleted) Explicitly default the special member function in question; if it would have been implicitly defined as deleted, the compiler will complain.

* points made in the comments by myself (1) and dyp (2)

64
votes

Nikos Athanasiou gave a good answer but I wanted to add this tool that I think is very useful.

Here is a screenshot of Howard Hinnant's presentation "Everything You Ever Wanted To Know About Move Semantics (and then some)" from ACCU 2014 conference which I think is a very good reminder of the rules of automatic generation of special members:

enter image description here

Clarification from Mr Hinnant from the comments:

The slide doesn't say it, but the red squares indicate deprecated behavior. I.e. if you don't want to depend upon deprecated behavior, then declare both of your copy members if you declare your destructor, or one of the copy members (basically follow the C++98/03 "rule of 3")

I recommend reading the slides to get the progressive construction of this table and have a detailed explanation of how and why we have this now.

Other presentations can be found there: http://accu.org/index.php/articles/1901