SFINAE при ошибке в зависимом типе вызывает непредвиденную серьезную ошибку - PullRequest
3 голосов
/ 27 марта 2020

У меня есть код, который я могу упростить до следующего вида:

#include <type_traits>

template <typename T>
struct dependent
{
    using type = typename T::type;
};

template <typename T>
typename dependent<T>::type
foo(const T& x);

bool foo(bool x) { return x; }

int main()
{
    foo(true);
}

Не удается скомпилировать с g ++ 9.3 с --std=c++17 с ошибкой:

test.cpp: In instantiation of 'struct dependent<bool>':
test.cpp:11:1:   required by substitution of 'template<class T> typename dependent<T>::type foo(const T&) [with T = bool]'
test.cpp:17:13:   required from here
test.cpp:6:11: error: 'bool' is not a class, struct, or union type
    6 |     using type = typename T::type;
      |           ^~~~

This это не то, что я ожидал. Я ожидаю, что попытка заменить bool на T в template <typename T> typename dependent<T>::type foo(const T& x) будет неудачей, а не ошибкой. Кажется, SFINAE не работает для меня, но я не знаю, почему.

Из примеров в неофициальной ссылке на SFINAE :

Замена продолжается в лексический порядок и останавливается при обнаружении сбоя.

template <typename A>
struct B { using type = typename A::type; };

template <
  class T,
  class   = typename T::type,      // SFINAE failure if T has no member type
  class U = typename B<T>::type    // hard error if T has no member type
                                   // (guaranteed to not occur as of C++14)
> void foo (int);

Я запускаю регистр на class U = typename B<T>::type, но бит "гарантированно не произойдет с C ++ 14", кажется, указывает что это не должно происходить с C ++ 14. Что дает?

1 Ответ

4 голосов
/ 27 марта 2020

Проблема в том, что dependent<T> имеет type, но это может быть плохо сформировано, что приводит к серьезному отказу.

Вы можете сделать dependent SFINAE дружественным:

template <typename T, typename Enabler = void>
struct dependent
{
};

template <typename T>
struct dependent<T, std::void_t<typename T::type>>
{
    using type = typename T::type;
};

Демо

...