“Inverse SFINAE” to avoid ambiguous overload

一笑奈何 提交于 2020-06-29 07:03:54

问题


How can I prevent the first template below from instantiating if the second template instantiates? (i.e. if both static_cast<T>(0) and T::zero() are defined)

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

回答1:


If you need to extend it to multiple overloads with fine grained control of overload rank, a common technique is to use tag dispatching.

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

Derived to base conversions will prefer the closest base class. Which translates to calling the overload with the highest rank. Since that one will have the best implicit conversion sequence in the eyes of the compiler.




回答2:


Without enable_if, relying on built-in rules of integer conversion ranks (conversion of 0 -> int is better than 0 -> char, which makes the former a first choice candidate, and the latter a viable second choice candidate):

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

With your own enable_if predicate (similar to the std::void_t technique):

#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



来源:https://stackoverflow.com/questions/60247980/inverse-sfinae-to-avoid-ambiguous-overload

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!