«Обратный SFINAE», чтобы избежать неоднозначных перегрузок - PullRequest
0 голосов
/ 16 февраля 2020

Как я могу предотвратить создание первого шаблона ниже, если создается второй шаблон? (т.е. если определены и 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();
}

Ответы [ 2 ]

3 голосов
/ 16 февраля 2020

Если вам необходимо расширить его до нескольких перегрузок с точным контролем ранга перегрузки, распространенным методом является использование диспетчеризации тегов.

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 голосов
/ 16 февраля 2020

Без 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

...