使用 enable_if 的 C++ 模板函数的默认回退
Default fallback for C++ template functions using enable_if
我想编写一个 C++ 机制,如果给定的 class Param
是从某个基 class.[=16= 派生的,则调用函数的不同实例化]
这与 std::is_base_of
和 std::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"
}
也就是说,在 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"
}
我想编写一个 C++ 机制,如果给定的 class Param
是从某个基 class.[=16= 派生的,则调用函数的不同实例化]
这与 std::is_base_of
和 std::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"
}
也就是说,在 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"
}