Резервный вариант по умолчанию для функций шаблона C++ с использованием enable_if

Я хочу написать механизм C++, в котором вызываются разные экземпляры функции, если заданный класс Paramпроисходит от определенного базового класса.

Это очень хорошо работает с std::is_base_ofа также std::enable_if.

Тем не менее, я хотел бы иметь "версию по умолчанию" этого doStuff()функция, которая вызывается для "каждого другого класса". В основном это будет работать, если сделать что-то вроде «если Param не получен из A и если не получен из B», но мне интересно, есть ли более элегантное решение.

      #include <iostream>

class A {};

class B : public A {};

class X {};

class Y : public X {};

class Other {};

template <typename Param, std::enable_if_t<std::is_base_of<A, Param>::value, bool> = true>
void doStuff() {std::cout << "Base A" << std::endl;};

template <typename Param, std::enable_if_t<std::is_base_of<X, Param>::value, bool> = true>
void doStuff() {std::cout << "Base X" << std::endl;};


int main()
{
    doStuff<B>();
    doStuff<Y>();
    // doStuff<Other>(); this is neither derived from B and Y, so call the default case
}

Решение должно работать с C++14.

2 ответа

Когда используешь std:::enable_if, вам нужно будет предоставить третью перегрузку SFINAE, которая обрабатывает условия по умолчанию, которые не обрабатываются другими перегрузками, например:

      #include <iostream>
#include <type_traits>

class A {};

class B : public A {};

class X {};

class Y : public X {};

class Other {};

template <typename Param, std::enable_if_t<std::is_base_of<A, Param>::value, bool> = true>
void doStuff() { std::cout << "Base A" << std::endl; }

template <typename Param, std::enable_if_t<std::is_base_of<X, Param>::value, bool> = true>
void doStuff() { std::cout << "Base X" << std::endl; }

template <typename Param, std::enable_if_t<!(std::is_base_of<A, Param>::value || std::is_base_of<X, Param>::value), bool> = true>
void doStuff() { std::cout << "Something else" << std::endl; }

int main()
{
    doStuff<B>(); // prints "Base A"
    doStuff<Y>(); // prints "Base X"
    doStuff<Other>(); // prints "Something else"
}

Онлайн-демонстрация

При этом в С++17 и более поздних версиях вы можете использовать if constexprвместо этого, что чище, чем использование SFINAE в этой ситуации, например:

      #include <iostream>
#include <type_traits>

class A {};

class B : public A {};

class X {};

class Y : public X {};

class Other {};

template <typename Param>
void doStuff() {
    if constexpr (std::is_base_of_v<A, Param>)
        std::cout << "Base A" << std::endl;
    else if constexpr (std::is_base_of_v<X, Param>)
        std::cout << "Base X" << std::endl;
    else
        std::cout << "Something else" << std::endl;
}

int main()
{
    doStuff<B>(); // prints "Base A"
    doStuff<Y>(); // prints "Base X"
    doStuff<Other>(); // prints "Something else"
}

Онлайн-демонстрация

Или, если вы можете использовать C++17 или более позднюю версию, используйте constexpr, гораздо более читабельный, чем SFINAE.

      #include <type_traits>
#include <iostream>

class A {};
class B : public A {};

class X {};
class Y : public X {};

class Other {};

template<typename type_t>
void doStuff()
{ 
    if constexpr (std::is_base_of_v<A,type_t>)
    {
        std::cout << "Base A\n";
        
    }
    else
    if constexpr (std::is_base_of_v<X, type_t>)
    {
        std::cout << "Base X\n";
    }
    else
    {
        std::cout << "Other\n";
    }
};

int main()
{
    doStuff<B>();
    doStuff<Y>();
    doStuff<Other>(); //this is neither derived from B and Y, so call the default case

    return 0;
}
Другие вопросы по тегам