Как я могу сделать так, чтобы C ++ предпочел соответствовать перегрузке родительского класса вместо шаблона? - PullRequest
0 голосов
/ 07 ноября 2018
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&?

Могу ли я изменить класс, чтобы он предпочитал приведение к шаблону, чтобы обходной путь не требовался?

Ответы [ 2 ]

0 голосов
/ 07 ноября 2018

Компилятор предпочитает выбирать конструктор шаблона с 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, то, как и в вашем примере кода, будет выбран конструктор шаблона. Я полагаю, что это преднамеренно.

0 голосов
/ 07 ноября 2018

Одним из решений является отключение конструктора шаблона через SFINAE:

template <
    typename T,
    std::enable_if_t<!std::is_base_of_v<Parent, T>> * = 0
>
Foo (const T &);
...