Given this class hierarchy:
#include <iostream>
class Base {
public:
Base() = default;
Base(const Base&) { std::cout << " copy\n"; }
template<typename T>
Base(T&&) { std::cout << " T&&\n"; }
};
class Sub : public Base {
public:
using Base::Base;
};
It is known that this code would print T&&
:
// objects
Base varObj;
const Base constObj;
// invoking constructors
Base copy(varObj); // T&&
Base move(std::move(constObj)); // T&&
Base number(42); // T&&
Why does using Sub
instead of Base
"fix" the problem?
// objects
Sub varObj;
const Sub constObj;
// invoking constructors
Sub copy(varObj); // copy
Sub move(std::move(constObj)); // copy
Sub number(42); // T&&
This is CWG2356: the inherited constructor is still a better match for a non-const input (because of the qualification conversion required), but it is discarded by [over.match.funcs]/8. Part of the reason for this is to avoid making any class that inherits constructors implicitly convertible from its base class.
There is still the possibility that the Sub
copy constructor (used by both of your initializations) would invoke the constructor template. However, its source parameter is a const Sub&
, so its base subobject is a const Base&
. That’s an exact match for the Base
copy constructor, so it’s selected over the template (which otherwise wins because of qualification conversions or reference kind).