vitaut
vitaut

Reputation: 55745

Why does this SFINAE give an error in gcc?

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

Answers (2)

Davis Herring
Davis Herring

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

solinent
solinent

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

Related Questions