【问题标题】:Why does this SFINAE give an error in gcc?为什么这个 SFINAE 在 gcc 中给出错误?
【发布时间】:2019-10-19 13:51:22
【问题描述】:

考虑以下示例 (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

【问题讨论】:

    标签: c++ c++11 gcc


    【解决方案1】:

    这是由 fixed retroactively 的语言缺陷造成的。虽然 T=S 在每个声明中都被立即替换,但没有理由(现在抽象性对于函数 type 并不重要)实例化 std::back_insert_iterator&lt;S&gt; 直到重载决议(需要知道如何构造),这永远不会发生,因为在为未命名的模板参数准备默认值时推导失败。标准中有一个 similar example 涉及一个返回类型,如果在检查之前推理没有失败(在这种情况下通过替换),这将是一个硬错误。

    【讨论】:

    • 这与 P0929R2 中的抽象类有什么关系(第一个链接)?
    • @vitaut:如果函数类型因为参数是抽象的而无效,那么 that 是 SFINAE-able 错误,因此类是否抽象很重要,它必须是实例化以便找出。
    • 那部分我明白了。我想知道 P0929R2 与我的示例有何关系。
    • @vitaut:您的示例的行为取决于迭代器类是否在演绎失败之前实例化并且函数模板被丢弃。 P0929R2 删除了关于函数 type 的规则(在替换过程中相关,因为它是 what SFINAE detects),因此抽象不再 affects the semantics of the program 并且不会发生实例化(直到重载决议,如果发生这种情况)。
    【解决方案2】:

    它看起来应该可以编译,因为整个函数都应该被实例化拒绝,并且编译器不应该继续实例化函数的参数模板。

    但是,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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-07
      • 1970-01-01
      • 2022-08-12
      相关资源
      最近更新 更多