16

Consider the following class:

class A
{
public:
   std::string field_a;
   std::string field_b;
}

Now consider the following copy construction:

A a1(a2);

The copy construction will adequately copy A despite the lack of of an explicit copy constructor because the copy constructors for std::string will be called by the compiler generated implicit copy constructor.

What I wish to know is, is the same true for move construction?

EDIT: Testing here shows that:

A a2(std::move(a1));

Will actually result in a copy construction, unless the specific move constructor:

A( A && other ) : a(std::move(other.a)) {}

Is defined.

EDIT EDIT I pinged Stephan T Lavavej and asked him why VC 2012 doesn't seem to follow what draft 12.8 states regarding implicit move constructor generation. He was kind enough to explain:

It's more of a "feature not yet implemented" than a bug. VC currently implements what I refer to as rvalue references v2.0, where move ctors/assigns are never implicitly generated and never affect the implicit generation of copy ctors/assigns. C++11 specifies rvalue references v3.0, which are the rules you're looking at.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Benj
  • 31,668
  • 17
  • 78
  • 127

2 Answers2

18

Yes, from the C++11 draft, 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,
  • X does not have a user-declared copy assignment operator,
  • X does not have a user-declared move assignment operator,
  • X does not have a user-declared destructor, and
  • the move constructor would not be implicitly defined as deleted.

The last condition is specified with more detail later:

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,
  • for the copy constructor, a non-static data member of rvalue reference type, or
  • for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.

Plainly speaking, the move constructor will be implicitly declared if:

  1. The class does not have user-declared any of the other special member functions.
  2. The move constructor can be sensibly implemented by moving all its members and bases.

Your class obviously complies with these conditions.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • So looking at the edit I appended, why am I not getting an implicit move construction? – Benj Nov 12 '12 at 14:37
  • Nitpick: from the draft I have (2011-02-28), this is section "12.8". – Olaf Dietsche Nov 12 '12 at 14:39
  • http://ideone.com/1tBOpe seems to show that for g++ an explicit move constructor is required. – Benj Nov 12 '12 at 14:42
  • @Benj: Maybe a compiler bug? Or maybe the code is not exactly as you copied. I tried it and it works for me with GCC 4.7.2. In ideone, however, the same code with GCC 4.5 does __not__ work. – rodrigo Nov 12 '12 at 14:44
  • @OlafDietsche: Mine too, a typo! Thanks! – rodrigo Nov 12 '12 at 14:44
  • @Benj: GCC 4.5.1, the version used in ideone is not very compliant with C++11. You should try at least GCC 4.7 to do serious job with the new features of C++11. – rodrigo Nov 12 '12 at 14:46
  • @rodrigo If this is a bug, VC2012 shares the same bug. – Benj Nov 12 '12 at 14:46
  • @Benj: Probably the C++11 specification changed some time ago, and these relatively-old compilers implemented the old criterion. Just guessing... – rodrigo Nov 12 '12 at 14:59
  • Note this is implemented now in [Visual C++2015](http://www.visualstudio.com/en-us/news/vs2015-preview-vs.aspx#C++) – Chuck Walbourn Feb 02 '15 at 23:39
6

The compiler synthesizes a move constructor if it can and if there is no user-defined copy constructor. The restriction that no move constructor is synthesized if there is copy constructor is intended to avoid breaking existing code. Of course, all members need to be movable. The exact rules are a bit more involved.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Won't there always be a copy constructor unless the auto generated one is specifically removed? So in other words, move construction of the `std::string` won't take place unless an explicit move constructor is defined. – Benj Nov 12 '12 at 14:16
  • @Benj: I guess, I'll slip in an "user-defined" at the opportune place. The synthesized copy constructors don't count. – Dietmar Kühl Nov 12 '12 at 14:27
  • So why does an explicit move constructor seem to be required in this program: http://ideone.com/1tBOpe (note the difference if the move constructor is commented out) – Benj Nov 12 '12 at 14:41
  • @Benj: What is this program supposed to show? It won't show whether the string is moved: the two object can be connected or can be disconnected. In particular, the moved from string can be left in a pretty random state. Also, on the compiler I tried it with (gcc and clang) both produce the same outputs independent of whether the move constructor is defined. You might be able to observe the moves with an instrumented, movable type. – Dietmar Kühl Nov 12 '12 at 14:53
  • Dietmar the `std::string` implementation my compiler uses leaves a moved from string looking like an empty string so it's possible to see if it's been moved by seeing if it's empty. – Benj Nov 12 '12 at 15:26
  • @Benj: In all combinations (using gcc and clang with their respective preferred standard libraries libstdc++ and libc++ and with or without the user-defined move constructor) the source ended up empty. – Dietmar Kühl Nov 12 '12 at 16:12
  • Yes and the source should end up empty in both cases. However it doesn't in gcc 4.5, nor in MSVC 2012. That's because these two don't yet implement rvalue references v3.0 which is draf 12.8. – Benj Nov 12 '12 at 16:27