为什么这个SFINAE会给出gcc错误?

时间:2019-06-04 03:31:18

标签: c++ c++11 gcc

请考虑以下示例(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);
}

使用gcc 8.3或更早版本进行编译会产生错误:

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)

       ^~~~~~~~

而clang和gcc 9编译时没有任何错误。此示例是否正确使用了SFINAE,并且是gcc <9中的错误?

2 个答案:

答案 0 :(得分:4)

这是由fixed retroactively的语言缺陷引起的。尽管T = S在每个声明中立即被替换,但是没有理由(现在,抽象对于函数 type 并不重要) std::back_insert_iterator<S>直到重载解析(它需要知道如何构造)为止,这永远不会发生,因为在为未命名模板参数准备默认值时推导失败。标准中有一个similar example涉及一个返回类型,如果推导在检查之前没有失败(在这种情况下为替代),这将是一个硬错误。

答案 1 :(得分:1)

看起来确实应该编译,因为出于这种原因应该拒绝整个函数,并且编译器不应继续实例化该函数的参数模板。

但是,clang编译了这段代码,而gcc 8.3拒绝了它(我的系统上没有gcc 9),因此看起来可能是一个错误。不过,该错误已在gcc 9中解决。

#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);
}