"Inverse SFINAE" 避免模棱两可的过载

"Inverse SFINAE" to avoid ambiguous overload

如果第二个模板实例化,如何防止下面的第一个模板实例化? (即如果同时定义了 static_cast<T>(0)T::zero()

template<typename T>
auto zero() ->decltype(static_cast<T>(0)) {
    return static_cast<T>(0);
}

template<typename T>
auto zero() ->decltype(T::zero()) {
    return T::zero();
}

没有enable_if,依赖整数转换行列的内置规则(0->int的转换优于0->char, 这使得前者成为第一选择候选人,而后者成为可行的第二选择候选人):

template <typename T>
auto zero_helper(char) -> decltype(static_cast<T>(0))
{
    return static_cast<T>(0);
}

template <typename T>
auto zero_helper(int) -> decltype(T::zero())
{
    return T::zero();
}

template <typename T>
auto zero() -> decltype(auto)
{
    return zero_helper<T>(0);
}

DEMO

使用您自己的 enable_if 谓词(类似于 std::void_t 技术):

#include <type_traits>

template <typename...>
struct voider { using type = void; };

template <typename... Ts>
using void_t = typename voider<Ts...>::type;

template <typename T, typename = void_t<>>
struct has_zero : std::false_type {};

template <typename T>
struct has_zero<T, void_t<decltype(T::zero())>> : std::true_type {};

template <typename T>
auto zero()
    -> typename std::enable_if<has_zero<T>::value, decltype(T::zero())>::type
{
    return T::zero();
}

template <typename T>
auto zero()
    -> typename std::enable_if<!has_zero<T>::value, T>::type
{
    return static_cast<T>(0);
}

DEMO 2

如果您需要通过对重载等级的细粒度控制将其扩展到多个重载,一种常用的技术是使用标签调度。

template<int r>
struct rank : rank<r - 1> {};

template<>
struct rank<0> {};

template<typename T>
auto zero_impl(rank<0>) -> decltype(static_cast<T>(0)) {
    return static_cast<T>(0);
}

template<typename T>
auto zero_impl(rank<1>) ->decltype(T::zero()) {
    return T::zero();
}

template<typename T>
auto zero() { return zero_impl<T>(rank<10>{}); }

派生到碱基的转换将优先选择最接近的碱基 class。这转化为调用具有最高级别的重载。因为在编译器的眼中,那个将具有最好的隐式转换序列。