15

I expected this to work

struct Parent
{
    Parent ();

    Parent (const Parent &);
};

struct Child : public Parent
{
    using Parent::Parent;
};

Parent p;

Child c (p);

Child inherits all of Parent's constructors, right?

Including Parent::Parent(const Parent &)?

x.cpp:15:11: error: no matching function for call to ‘Child::Child(Parent&)’
 Child c (p);
           ^
x.cpp:5:2: note: candidate: Parent::Parent(const Parent&)
  Parent (const Parent &);
  ^~~~~~
x.cpp:10:16: note:   inherited here
  using Parent::Parent;
                ^~~~~~
x.cpp:10:16: note:   an inherited constructor is not a candidate for initialization from an expression of the same or derived type
x.cpp:8:8: note: candidate: Child::Child()
 struct Child : public Parent
        ^~~~~

Why can't I construct a Child from a Parent?

spraff
  • 32,570
  • 22
  • 121
  • 229

2 Answers2

7

Inheriting a constructor doesn't prevent the default copy constructor of Child from being generated by the compiler.

This implies that you have a Child::Child(const Child&) which is hiding the inherited constructor that can't be chosen by lookup resolution, as cppreference.com explains:

if an inherited constructor matches the signature of one of the constructors of Derived, it is hidden from lookup by the version found in Derived. If one of the inherited constructors of Base happens to have the signature that matches a copy/move constructor of the Derived, it does not prevent implicit generation of Derived copy/move constructor (which then hides the inherited version, similar to using operator=).

In §12.9 of C++11 ISO standard is it stated:

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 class where the using-declaration appears.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • 1
    I don't think the inherited constructor is being hidden, they don't have the same signature. – Mansoor Sep 13 '19 at 15:19
  • 1
    it's hidden in the sense that it's not inherited at all, there is no `Child::Child(const Parent&)` in the final class because the compiler is not generating it, since there's already a `Child::Child(const Child&)` – Jack Sep 13 '19 at 15:22
  • 1
    Maybe I'm being confused by your wording, the compiler would not auto-generate a `Child::Child(const Parent&)` in any case? You would need to define that yourself? – Mansoor Sep 13 '19 at 15:25
  • 1
    @Mansoor The compiler generates `Child::Child(const Child&)`. Because it does so, `Child::Child(const Parent&)` is not created because as this code shows, you would create a child from a parent and non of the child part of the class would be initialized. If you want that behavior, you have to manually do it. – NathanOliver Sep 13 '19 at 15:29
  • @NathanOliver " Because it does so, Child::Child(const Parent&) is not created", not created by who? The compiler would not do it by default and it is not defined in the Child class? – Mansoor Sep 13 '19 at 15:38
  • 2
    @Mansoor by the `using Parent::Parent` directive which tells the compiler to inherit constructors from parent class, indeed. – Jack Sep 13 '19 at 15:40
  • @Mansoor Having `using Parent::Parent;` basically says hey compiler, pretend you have `Child::Child(const Parent&)` and call `Parent (const Parent &);`. But because the compiler will automatically generate a copy constructor for `Child` (`Child::Child(const Child&)`), the copy constructor hides the synthetic constructor the compiler would make from the inheritance. – NathanOliver Sep 13 '19 at 15:42
  • 1
    Note that the specification for inheriting constructors has significantly changed after C++11 (in C++17 as a Defect Report) – cmeerw May 03 '21 at 15:47
5

This is essentially CWG issue 2356: Base class copy and move constructors should not be inherited.

[over.match.funcs]/p9 now says:

A constructor inherited from class type C ([class.inhctor.init]) that has a first parameter of type “reference to cv1 P” (including such a constructor instantiated from a template) is excluded from the set of candidate functions when constructing an object of type cv2 D if the argument list has exactly one argument and C is reference-related to P and P is reference-related to D.

cmeerw
  • 7,176
  • 33
  • 27