Полиморфизм в шаблонных функциях - PullRequest
1 голос
/ 25 марта 2019

Я хочу использовать шаблонную функцию для обработки как полиморфных, так и неполиморфных классов. Вот 3 основных класса.

class NotDerived
{

};

class Base
{
public:
    virtual ~Base() {}
    void base_method() {}
};

class Derived : public Base
{

};

Поскольку NotDerived не имеет виртуальных функций, я не могу использовать dynamic_cast, следующая функция шаблона:

template<class T>
auto foo(T& some_instance)
{
    if (std::is_base_of_v<Base, T>)
    {
        //CASE_1: Works for d and b
        /*some_instance.base_method();*/

        //CASE_2: Works for d and b
        /*auto lamb1 = [](T& some_instance) {some_instance.base_method(); };
        lamb1(some_instance);*/


        auto lamb2 = [](T& some_instance) {((Base&)some_instance).base_method(); };
        lamb2(some_instance);
    }
}

Основные функции делают это:

void main()
{
    Derived d{};
    Base b{};
    NotDerived nd{};

    foo(d);
    foo(b);
    foo(nd);
}

Теперь я понимаю, почему CASE_1 не работает в случае передачи переменной nd, но я не могу понять, что мне нужно явно привести some_instance в функцию lamb2, чтобы вызвать base_method.

Может кто-нибудь объяснить, почему CASE_1, CASE_2 не работают, а CASE_3 работает. Под работой я имею в виду вызов base_method, если это возможно, без dynamic_casting.

Также возможно ли использовать constexpr для обработки таких случаев статического полиморфизма или скомпилированного полиморфизма (надеюсь, это будет правильно называть так)

Ответы [ 2 ]

5 голосов
/ 25 марта 2019

Случай 3 не работает.Вы приводите несвязанный тип и используете это временное значение для вызова функции с неопределенным поведением.

Проблема с использованием

if (std::is_base_of_v<Base, T>)
{
    //...
}

состоит в том, что все в части ... должнобыть в состоянии скомпилировать.Вам нужен способ вызывать этот код только тогда, когда T - это тот тип, который вам нужен.Вы можете использовать constexpr, если как

if constexpr (std::is_base_of_v<Base, T>)
{
    some_instance.base_method();
}
else
{
    // do something else since T isn't a Base or derived from Base
}

А теперь, если std::is_base_of_v<Base, T> равно false, some_instance.base_method(); будет отброшено и никогда не будет скомпилировано.

1 голос
/ 25 марта 2019

Вы должны взглянуть на официальный документ о SFINAE . Код внутри вашего if будет скомпилирован в любом случае, поэтому он не будет компилироваться.

Если вы используете C ++ 17, вы можете заменить его на constexpr if, который будет оценивать информацию внутри if во время компиляции и игнорировать код при необходимости:

template<class T>
auto foo(T& some_instance) {
    if constepxr (std::is_base_of_v<Base, T>) {
        auto lamb2 = [](T& some_instance) {((Base&)some_instance).base_method(); }
        lamb2(some_instance);
    } else {
        // Whatever you want to do
    }
}

Если вы используете более старую версию C ++, простая перегрузка функции может решить вашу проблему:

template<class T>
auto foo(T& some_instance) {
   // Do whatever
}

auto foo(Base& some_instance) {
    auto lamb2 = [](T& some_instance) {((Base&)some_instance).base_method(); }
    lamb2(some_instance);
}

В качестве альтернативы вы можете использовать механизм SFINAE для просмотра нужной функции с помощью std::enable_if:

template <typename T>
typename std::enable_if<std::is_base_of<Base, T>::value, void>::type
foo() {
   auto lamb2 = [](T& some_instance) {((Base&)some_instance).base_method(); }
   lamb2(some_instance); 
}

template <typename T>
typename std::enable_if<!std::is_base_of<Base, T>::value, void>::type
foo() {
   // Do whatever
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...