130

Is this

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB}               { }
    Example(const Example& mE) : a{mE.a}, b{mE.b}        { }
    Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
    Example& operator=(const Example& mE) { a = mE.a; b = mE.b; return *this; } 
    Example& operator=(Example&& mE)      { a = move(mE.a); b = move(mE.b); return *this; } 
}

equivalent to this

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB} { }
    Example(const Example& mE)            = default;
    Example(Example&& mE)                 = default;
    Example& operator=(const Example& mE) = default;
    Example& operator=(Example&& mE)      = default;
}

?

Pharap
  • 3,161
  • 4
  • 31
  • 44
Vittorio Romeo
  • 82,972
  • 25
  • 221
  • 369
  • This might be a duplication of http://stackoverflow.com/questions/4819936/why-no-default-move-assignment-move-constructor –  Aug 17 '13 at 15:55
  • 9
    @DieterLücking: It's clearly not, though it's on a similar topic and some answers may cover similar ground. However, we shall not close every single question about move semantics as duplicates of each other. – Lightness Races in Orbit Aug 18 '13 at 01:45
  • 1
    Note, I added my answer to this question because at the time I was looking for a quote from the standard that proved they were equivalent and the accepted answer does not do that. So, I just found the quote and added my answer. – Shafik Yaghmour Feb 02 '18 at 16:35
  • I also want to mention, that in your Example the _default constructor_ is **not declared** and the _destructor_ is **defaulted** - See [Howard Hinnant - compiler implicit declares](https://i.stack.imgur.com/C2EUm.png) – thomas.st Feb 02 '20 at 13:03

4 Answers4

65

Yes both are the same.

But

struct Example { 
    int a, b; 
    Example(int mA, int mB) : a{mA}, b{mB} { }
    Example(const Example& mE)            = default;
    Example(Example&& mE)                 = default;
    Example& operator=(const Example& mE) = default;
    Example& operator=(Example&& mE)      = default;
}

This version will permits you to skip the body definition.

However, you have to follow some rules when you declare explicitly-defaulted-functions :

8.4.2 Explicitly-defaulted functions [dcl.fct.def.default]

A function definition of the form:

  attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ;

is called an explicitly-defaulted definition. A function that is explicitly defaulted shall

  • be a special member function,

  • have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const T”, where T is the name of the member function’s class) as if it had been implicitly declared,

  • not have default arguments.

Community
  • 1
  • 1
Pierre Fourgeaud
  • 13,652
  • 1
  • 34
  • 58
  • 1
    What document are you quoting 8.4.2 from? Neither the C++11 standard or N3690 contain the text ", and not have an *exception-specification*" in 8.4.2/1. They do both say in 8.4.2/2: "An explicitly-defaulted function may be declared `constexpr` only if it would have been implicitly declared as `constexpr`, and may have an explicit *exception-specification* only if it is compatible (15.4) with the *exception-specification* on the implicit declaration." – Casey Aug 17 '13 at 22:45
  • 2
    @Casey Good catch ! I was quoting the N3242... I mixed up my docs... I updated my post to quote the N3690 ! Thank you for pointing this out ! – Pierre Fourgeaud Aug 17 '13 at 22:55
  • 2
    If I set a move constructor and assignment operator to `= default`, will I be able to swap with the object? Don't I need to declare the constructor as `noexcept`? I tried putting both `noexcept` and `=default` for both, but this would not compile. – VF1 Aug 23 '13 at 19:21
  • Declaring copy constructor or assignment or destructor prevents default move constructor generation. If we define any of these functions,we have to define move constructor. But how about the case copy constructor is defined but move constructor is created by default keyword.Does it work as expected? – Muhammet Ali Asan Dec 23 '18 at 23:31
28

Yes, a defaulted move constructor will perform a member-wise move of its base and members, so:

Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }

is equivalent to:

Example(Example&& mE)                 = default;

we can see this by going to the draft C++11 standard section 12.8 Copying and moving class objects paragraph 13 which says (emphasis mine going forward):

A copy/move constructor that is defaulted and not defined as deleted is implicitly defined if it is odrused (3.2) or when it is explicitly defaulted after its first declaration. [ Note: The copy/move constructor is implicitly defined even if the implementation elided its odr-use (3.2, 12.2). —end note ][...]

and paragraph 15 which says:

The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. See also the example in 12.6.2. —end note ] The order of initialization is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2). Let x be either the parameter of the constructor or, for the move constructor, an xvalue referring to the parameter. Each base or non-static data member is copied/moved in the manner appropriate to its type:

  • if the member is an array, each element is direct-initialized with the corresponding subobject of x;
  • if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
  • otherwise, the base or member is direct-initialized with the corresponding base or member of x.

Virtual base class subobjects shall be initialized only once by the implicitly-defined copy/move constructor (see 12.6.2).

Shafik Yaghmour
  • 143,425
  • 33
  • 399
  • 682
24

Is a =default move constructor equivalent to a member-wise move constructor?

Yes. Update: Well, not always. Look at this example:

#include <iostream>

struct nonmovable
{
    nonmovable() = default;

    nonmovable(const nonmovable  &) = default;
    nonmovable(      nonmovable &&) = delete;
};

struct movable
{
    movable() = default;

    movable(const movable  &) { std::cerr << "copy" << std::endl; }
    movable(      movable &&) { std::cerr << "move" << std::endl; }
};

struct has_nonmovable
{
    movable    a;
    nonmovable b;

    has_nonmovable() = default;

    has_nonmovable(const has_nonmovable  &) = default;
    has_nonmovable(      has_nonmovable &&) = default;
};

int main()
{
    has_nonmovable c;
    has_nonmovable d(std::move(c)); // prints copy
}

It prints:

copy

http://coliru.stacked-crooked.com/a/62c0a0aaec15b0eb

You declared defaulted move constructor, but copying happens instead of moving. Why? Because if a class has even a single non-movable member then the explicitly defaulted move constructor is implicitly deleted (such a pun). So when you run has_nonmovable d = std::move(c), the copy constructor is actually called, because the move constructor of has_nonmovable is deleted (implicitly), it just doesn't exists (even though you explicitly declared the move constructor by expression has_nonmovable(has_nonmovable &&) = default).

But if the move constructor of non_movable was not declared at all, the move constructor would be used for movable (and for every member that has the move constructor) and the copy constructor would be used for nonmovable (and for every member that does not define the move constructor). See the example:

#include <iostream>

struct nonmovable
{
    nonmovable() = default;

    nonmovable(const nonmovable  &) { std::cerr << "nonmovable::copy" << std::endl; }
    //nonmovable(      nonmovable &&) = delete;
};

struct movable
{
    movable() = default;

    movable(const movable  &) { std::cerr << "movable::copy" << std::endl; }
    movable(      movable &&) { std::cerr << "movable::move" << std::endl; }
};

struct has_nonmovable
{
    movable    a;
    nonmovable b;

    has_nonmovable() = default;

    has_nonmovable(const has_nonmovable  &) = default;
    has_nonmovable(      has_nonmovable &&) = default;
};

int main()
{
    has_nonmovable c;
    has_nonmovable d(std::move(c));
}

It prints:

movable::move
nonmovable::copy

http://coliru.stacked-crooked.com/a/420cc6c80ddac407

Update: But if you comment out the line has_nonmovable(has_nonmovable &&) = default;, then copy will be used for both members: http://coliru.stacked-crooked.com/a/171fd0ce335327cd - prints:

movable::copy
nonmovable::copy

So probably putting =default everywhere still makes sense. It doesn't mean that your move expressions will always move, but it makes chances of this higher.

One more update: But if comment out the line has_nonmovable(const has_nonmovable &) = default; either, then the result will be:

movable::move
nonmovable::copy

So if you want to know what happens in your program, just do everything by yourself :sigh:

anton_rh
  • 5,990
  • 1
  • 31
  • 58
  • The explicitly defaulted move constructor is *deleted* in your first example, because that's what an implicit move constructor would be. It is *overload resolution* that disambiguates between the copy and move when presented with an rvalue – Caleth Oct 03 '18 at 09:18
  • The *result* of `has_nonmovable(has_nonmovable&&)= default` is a deleted move constructor, because of the `= delete`'d move constructor in a member. You ask for a move constructor that matches what would be generated by default, and get that. – Caleth Oct 03 '18 at 11:06
  • @Caleth, ok, I understand now. But I'm not sure you are right. If the move constructor of `has_nonmovable` was deleted, the compilation would fail. See this link: http://coliru.stacked-crooked.com/a/8f8c387cd45c0de1. The error there is: `use of deleted function`. I don't think that the result would be different if _C++_ had _**implicitly** deleted_ move constructors. – anton_rh Oct 03 '18 at 11:14
  • 2
    That's the difference between *deleted implicitly-declared* and *explicitly deleted*. When you explicitly delete a special member, it remains in the overload set, and the program is ill-formed if it would be selected. When it is implicitly deleted, it is removed from the overload set. See [Deleted implicitly-declared move constructor](https://en.cppreference.com/w/cpp/language/move_constructor) – Caleth Oct 03 '18 at 11:18
  • In your second example, `has_nonmovable(has_nonmovable&&)` does a memberwise move, and overload resolution only finds `nonmovable(const nonmovable &)` for `b` – Caleth Oct 03 '18 at 11:23
  • @Caleth, Well, You are right most likely: http://coliru.stacked-crooked.com/a/064cada42675e388. Then that explains what happens in the first example. – anton_rh Oct 03 '18 at 11:24
  • @Caleth, but this means that an explicitly defaulted move constructor is not always _equivalent to a member-wise move constructor_ (the title of the current question). Sometimes _explicitly defaulted_ means just _implicitly deleted_. – anton_rh Oct 03 '18 at 11:30
  • 1
    If and only if the member wise move would be ill-formed, so I don't count it as different – Caleth Oct 03 '18 at 11:31
  • 4
    @Caleth, But I do. For me, it would be better if a compiler just gave me the message: `cannot declare explicitly defaulted move constructor because it would be ill-formed`. Without this I just think that my **move**-expression moves when it is actually not a move expression at all because the move constructor was implicitly deleted by compiler. Explicit expression makes implicit things. This confuses very much. – anton_rh Oct 03 '18 at 11:45
-2

apart very pathological cases ... YES.

To be more precise, you have also to considered eventual bases Example may have, with exact same rules. First the bases -in declaration order- then the members, always in declaration order.

Emilio Garavaglia
  • 18,858
  • 2
  • 41
  • 60
  • 1
    But code cannot change the order of subobject construction. The language ignores the order of the constructor's member-initializer list and always constructs (and destructs) class subobjects in a consistent order. So changing that will not cause a constructor to not be equivalent. – aschepler Oct 03 '18 at 12:34