使用 enable_if 的 C++ 模板函数的默认回退

Default fallback for C++ template functions using enable_if

我想编写一个 C++ 机制,如果给定的 class Param 是从某个基 class.[=16= 派生的,则调用函数的不同实例化]

这与 std::is_base_ofstd::enable_if 配合使用非常好。

但是,我想要这个 doStuff() 函数的“默认版本”,它被“每隔一个 class”调用。 这基本上可以通过执行类似“如果 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。

或者如果你可以使用 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;
}

使用 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"
}

Online Demo

也就是说,在 C++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"
}

Online Demo