20

I've got the following code:

class C {
public:
    C(int) {}
    C(const C&) {}
    C() {}
};  

class D : public C { 
public:
    using C::C;
};  

int main() {
    C c;
    D d_from_c(c); // does not compile, copy ctor is not inherited
    D d_from_int(1); // compiles, C(int) is inherited
}   

Derived class should inherit all ctors of base except the default ctor (it is explained here). But why copy ctor is not inherited as well? Arguments from the related question are not acceptable here.

The code is compiled with g++ 4.8.1.

Community
  • 1
  • 1
Ivan Smirnov
  • 4,365
  • 19
  • 30

3 Answers3

21

Because the standard says so. [class.inhctor]/p3, emphasis mine:

For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same signature in the complete class where the using-declaration appears or the constructor would be a default, copy, or move constructor for that class.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • 4
    This rule has been retroactively replaced with (what is currently) [over.match.funcs]/8. Could you update your answer? – Davis Herring Aug 15 '19 at 07:14
13

Derived class should inherit all ctors of base except the default ctor

No, that's not true, see T.C.'s answer for the real rule.

The purpose of inheriting constructors is to say "the derived type can be created from the same arguments as the base type", but that isn't relevant for the base class' copy constructor, because a copy constructor is not just a way of saying how to create a type from a given argument.

A copy constructor is special, it's for copying an object of the same type.

A constructor D(const C&) would not used be for copying an object of the same type, because C is not the same type as D.

Community
  • 1
  • 1
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • 1
    This argument is flawed, in the same sense that inherited ctors of base class is semantically only responsible for initializing the base sub-object, the inherited copy ctor of base class is also responsible for initializing the base sub-object. Whether the initializing is from a set of params or the "other" object is irrelevant, the later being a special case of the former. I think the real reason for this restriction is "just to be cautious", although I really don't get what they're cautious about. – Weipeng Feb 08 '19 at 19:25
  • 1
    No the copy constructor is responsible for initializing the base part, by invoking the base copy constructor. You don't need an inheriting ctor for that though. – Jonathan Wakely Feb 08 '19 at 19:57
2

For a moment, we’ll assume ‘copy constructor inheritance’ is allowed. Having your class structure intact, please consider following code for modified main method.

int main() {
    C c;
    D d;
    D d_from_d(d);
    D d_from_c(c); // does not compile, copy ctor is not inherited
    D d_from_int(1); // compiles, C(int) is inherited
}  

In D d_from_d(d), as a normal constructor call, there will be two copy constructor calls. One for C::C(const C&) and the other one is for compiler generated copy constructor for D. Having source object type in D (d in this case), C’s copy constructor can copy d’s C attributes while compiler generated D’s copy constructor can copy d’s D attribute.

But in D d_from_c(c) case, There is no problem for C’s copy constructor because, c’s C attributes can be copies by C’s copy constructor. But how does the compiler generated D’s copy constructor know the way to copy ‘D’s attributes from C’s object’. This is a conflict which should be avoided.

But, if you provide some sort of ‘weird copy constructor’ (you may need to a default constructor as well) like;

D(const C & c):C(c){} 

Then, calling D d_from_c(c); is valid. Because, now we have explicitly provided a matching ‘copy’ constructor.

So, saying ‘Inheriting copy constructors are now allowed’ is invalid.

Doonyx
  • 580
  • 3
  • 12