Inherited constructor cannot be used to copy object

Viewed 77

This program does not compile:

template <class T>
struct Base {
    Base();

    template <class U>
    Base(const Base<U>&);
};


template <class T>
struct Doh : Base<T> {
    using Base<T>::Base;
};


template <class T>
struct Derp : Base<T> {
    using Base<T>::Base;
};


Doh<void> x = Derp<void>();

The error message says inherited constructor cannot be used to copy object. Live demo.

But when we change the last line to this, it compiles.

Doh<void> doh;
Doh<void> x1 = doh;
Doh<void> x2 = Derp<void*>();
Doh<void> x3(Derp<void>());

In these three cases the inherited constructor can be used. Why?

1 Answers

Because that is how it is prescribed to work.

[over.match.funcs/9] 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.

Here D = Doh<void>, C = Base<void> and the constructor of interest is the constructor with U = void which make P = Base<void>. That constructor is not available, and no other one fits.

Doh<void> x1 = doh;

This is OK because it uses the defaulted copy constructor of Doh<void>.

Doh<void> x2 = Derp<void*>();

This is OK because Base<void*> is not reference-related to Doh<void>.

The "point" of this language is that they want to exclude things that look like copy/move constructors. A Base template constructor could otherwise be greedy and be a better match than a provided or defaulted Derived copy/move constructor which would potentially be surprising.


Updated:

Doh<void> x3(Derp<void>());

This does not declare and initialize a Doh<void>. It declares a function x3 returning a Doh<void> and taking a single argument which is a function which takes no arguments and returns Derp<void>. It is a typical example of the "most vexing parse".

To declare and initialize a Doh<void>, you can use braces:

Doh<void> x3{Derp<void>()};

This fails to compile as expected.

Related