class Parent {};
class Child : public Parent {};
class Foo
{
public:
Foo (Parent &) {};
template <typename T>
Foo (const T &);
};
int main ()
{
Child c;
Foo foo (c);
}
Это приводит к ошибке компоновщика, так как конструктор для foo
выбирает template<typename T>Foo::Foo(const T &)
вместо Foo::Foo(Parent&)
,
Если c
имеет тип Parent
вместо Child
, это использует не шаблонный конструктор и ссылки без проблем.
Я могу обойти это с
Foo foo ((Parent&) c);
но я не хочу этого делать
Почему C ++ предпочитает использовать шаблон вместо неявного приведения c
в Parent&
?
Могу ли я изменить класс, предпочитая приведение типов к шаблону, чтобы обходной путь не был необходим?
Одним из решений является отключение конструктора шаблона через SFINAE:
template <
typename T,
std::enable_if_t<!std::is_base_of_v<Parent, T>> * = 0
>
Foo (const T &);
Компилятор предпочитает выбирать конструктор шаблона с T=child
потому что разрешение перегрузки считает, что преобразование квалификации (добавление const
к типу аргумента) лучше, чем производное в базовое преобразование.
Таким образом, самый простой способ — просто объявить конструктор, принимающий в качестве аргумента дочерний элемент:
class Foo
{
public:
Foo (Parent &) {};
Foo (Child & x):Foo(static_cast<Parent&>(x)) {};
template <typename T>
Foo (const T &);
};
Обратите внимание, что если аргументом конструктора является const lvalue или rvalue, то, как и в вашем примере кода, будет выбран конструктор шаблона. Я полагаю, что это намеренно.