Reputation: 55745
Consider the following example (https://godbolt.org/z/pSTUZI):
#include <iterator>
#include <type_traits>
template <typename T>
struct falsy : std::false_type {};
template <
typename T,
typename std::enable_if<falsy<T>::value, int>::type = 0>
void f(std::back_insert_iterator<T>) {}
template <typename T>
void f(T) {}
struct S {};
int main() {
S s;
f<S>(s);
}
Compiling it with gcc 8.3 or earlier gives an error:
In file included from /opt/compiler-explorer/gcc-8.3.0/include/c++/8.3.0/iterator:63,
from <source>:1:
/opt/compiler-explorer/gcc-8.3.0/include/c++/8.3.0/bits/stl_iterator.h: In instantiation of 'class std::back_insert_iterator<S>':
<source>:19:9: recursively required by substitution of 'template<class T, typename std::enable_if<falsy<T>::value, int>::type <anonymous> > void f(std::back_insert_iterator<_Container>) [with T = S; typename std::enable_if<falsy<T>::value, int>::type <anonymous> = <missing>]'
<source>:19:9: required from here
/opt/compiler-explorer/gcc-8.3.0/include/c++/8.3.0/bits/stl_iterator.h:490:7: error: no type named 'value_type' in 'struct S'
operator=(const typename _Container::value_type& __value)
^~~~~~~~
/opt/compiler-explorer/gcc-8.3.0/include/c++/8.3.0/bits/stl_iterator.h:497:7: error: no type named 'value_type' in 'struct S'
operator=(typename _Container::value_type&& __value)
^~~~~~~~
while clang and gcc 9 compile it without any errors. Is this example a correct use of SFINAE and is it a bug in gcc < 9?
Upvotes: 5
Views: 422
Reputation: 40053
This results from a language defect that was fixed retroactively. While T
=S
is substituted immediately in each declaration, there’s no reason (now that abstractness is not important to a function type) to instantiate std::back_insert_iterator<S>
until overload resolution (which needs to know how one might be constructed), which never happens because deduction fails when preparing the default value for the unnamed template parameter. There is a similar example in the standard involving a return type that would be a hard error if deduction didn’t fail before it is examined (by substitution in this case).
Upvotes: 4
Reputation: 1653
It does look like it should compile, as the entire function should be rejected for that insantiation and the compiler should not proceed to instantiate the function's argument templates.
However, clang compiles this code, while gcc 8.3 rejects it (no gcc 9 on my system), so it looks like it might be a bug. A bug that is squashed in gcc 9, though.
#include <type_traits>
template <typename T>
struct falsy : std::false_type {};
template <typename T>
struct back_insert_iterator {
back_insert_iterator&
operator=(typename T::value_type&& __value)
{
}
};
template <
typename T,
typename std::enable_if<falsy<T>::value, int>::type = 0>
void f(back_insert_iterator<T>) {
back_insert_iterator<T>::value;
}
template <typename T>
void f(T) { }
struct S {};
int main() {
S s;
f<S>(s);
}
Upvotes: 1