I have code that I can simplify down to something like this:
#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);
}
This fails to compile with g++ 9.3 with --std=c++17
with the error:
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 is not what I would expect. I would expect that attempting to substitute bool
for T
in template <typename T> typename dependent<T>::type foo(const T& x)
would be a failure, which is not an error. It seems SFINAE is not working for me, but I do not know why.
From the examples in the unofficial reference on SFINAE:
Substitution proceeds in lexical order and stops when a failure is encountered.
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);
I am hitting the case on class U = typename B<T>::type
, but the "guaranteed to not occur as of C++14" bit seems to indicate that this should not be happening as of C++14. What gives?
Issue is that dependent<T>
has type
, but that one might be ill formed causing hard failure.
You might make dependent
SFINAE friendly:
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;
};