12

In C++11, if the base class has defined its own move (copy) constructor (assignment operator), does its subclass need to define its own move (copy) constructor (assignment operator) in where call the base class's corresponding constructor/operator is called explicitly?

Is it a good idea to define the constructor, destructor, move/copy constructor (assignment operator) clearly every time?

struct Base {
    Base() {}
    Base(Base&& o);
};

struct Sub : public Base {
    Sub(Sub&& o) ;  // Need I do it explicitly ? If not,what the compiler will do for me
};
Niall
  • 30,036
  • 10
  • 99
  • 142
hl1020
  • 529
  • 5
  • 11
  • The compiler will generate a default move constructor if you don't have any, but not always (see e.g. [this reference for a list of when it's not created](http://en.cppreference.com/w/cpp/language/move_constructor)). However, you can't rely on the default move (or any other) constructor to "do the right thing", and especially you can't rely on the base class move constructor as it has no knowledge of any sub-classes. Also read about [the rule of three (or five since C++11)](https://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29). – Some programmer dude Sep 04 '14 at 11:52
  • 1
    @Puppy I would say that the rule of three and the rule of zero complements each other, not that one excludes or supersedes the other. Just because you can follow the rule of zero for some classes, doesn't mean you can (or should) do it for all classes. – Some programmer dude Sep 04 '14 at 12:10
  • 2
    Oh, and for reference to the OP (and other curious) [here's the rule of zero](http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html). – Some programmer dude Sep 04 '14 at 12:11
  • From a practical standpoint, as of now (year 2014), many compilers (especially MSVC) in C++11 mode do not generate default move constructors properly, so it is better to always write your own. – Siyuan Ren Sep 04 '14 at 12:11
  • 1
    @c.r. many? I am aware of one (MSVC) that pretends to be C++11 that lacks that. – Yakk - Adam Nevraumont Sep 04 '14 at 12:18

3 Answers3

6

The compiler will generate a default move constructor if you don't specify one in the base class (except some cases, e.g. there's a base class with a deleted move constructor) but you should, in any case, call explicitly the base class' one if you have it:

Sub(Sub&& o) : Base(std::move(o))
Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • 2
    How is that perfect fowarding? `Sub&&` is an rvalue-reference, so it's only going to get forwarded as an rvalue. If you used a universal-reference and `std::forward` that would be perfect forwarding. – David G Sep 04 '14 at 12:08
  • @0x499602D2 without perfect forwarding OP's code would generate an error because the copy constructor is implicitly deleted in Base, am I missing something? – Marco A. Sep 04 '14 at 12:11
  • 1
    Sorry I got your point: only rvalues can be passed there. I agree, not really pertaining to perfect forwarding in the sense of the term. A std::move could suffice here – Marco A. Sep 04 '14 at 12:14
  • 2
    Yes, and also there is an implicit coversion to a reference to the base class so there's no need for the explicit template arguments in `std::move()`. – David G Sep 04 '14 at 12:16
1

According to the standard (N3797) 12.8/9 Copying and moving class objects [class.copy]:

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.

As such, if your class meets the above requirements then a default move constructor will be implicitly declared for you.

As already being stated, the base-class has no knowledge of any sub-classes. As such, whether you declare a move constructor in one base class has no effect on the implicit generation of a move constructor in its sub-classes.

As far as it concerns whether you should declare explicitly a constructor/destructor etc. of a class, there's this nice article.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
101010
  • 41,839
  • 11
  • 94
  • 168
0

No, you don't have. I'll be automatically generated like default/copy constructor.


From this page,

Implicitly-declared move constructor

If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:

there are no user-declared copy constructors
there are no user-declared copy assignment operators
there are no user-declared move assignment operators
there are no user-declared destructors
(until C++14) the implicitly-declared move constructor is not defined as deleted due to conditions detailed in the next section 

then the compiler will declare a move constructor as an inline public member of its class with the signature T::T(T&&).

A class can have multiple move constructors, e.g. both T::T(const T&&) and T::T(T&&). If some user-defined move constructors are present, the user may still force the generation of the implicitly declared move constructor with the keyword default.

Your struct Sub has no user-declared copy constructors, copy assignment operators, move assignment operators or destructors.

And,

Trivial move constructor

The move constructor for class T is trivial if all of the following is true:

It is not user-provided (meaning, it is implicitly-defined or defaulted), and if it is defaulted, its signature is the same as implicitly-defined
T has no virtual member functions
T has no virtual base classes
The move constructor selected for every direct base of T is trivial
The move constructor selected for every non-static class type (or array of class type) member of T is trivial 

T has no non-static data members of volatile-qualified type 

(since C++14)

A trivial move constructor is a constructor that performs the same action as the trivial copy constructor, that is, makes a copy of the object representation as if by std::memmove. All data types compatible with the C language (POD types) are trivially movable.

Implicitly-defined move constructor

If the implicitly-declared move constructor is neither deleted nor trivial, it is defined (that is, a function body is generated and compiled) by the compiler. For union types, the implicitly-defined move constructor copies the object representation (as by std::memmove). 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.

The move constructor of Base is not trivial (it's user-defined). So, the implicitly-defined move constructor of Sub will work as "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."

Community
  • 1
  • 1
ikh
  • 10,119
  • 1
  • 31
  • 70