【问题标题】:"Inverse SFINAE" to avoid ambiguous overload“逆 SFINAE” 避免模棱两可的过载
【发布时间】:2020-02-16 11:26:10
【问题描述】:

如果第二个模板实例化,如何防止下面的第一个模板实例化? (即如果 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();
}

【问题讨论】:

标签: c++ templates c++14 sfinae


【解决方案1】:

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

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>{}); }

派生到基类的转换会优先选择最接近的基类。这意味着调用具有最高等级的重载。因为那个在编译器眼中将有最好的隐式转换序列。

【讨论】:

    【解决方案2】:

    没有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

    【讨论】:

    • zero() 应该返回 decltype(auto) 以保留 OP 的返回类型,以防它们是引用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    • 1970-01-01
    • 2014-06-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    相关资源
    最近更新 更多