为什么我的SFINAE表达式不再适用于GCC 8.2?

zen*_*hoy 19 c++ gcc sfinae language-lawyer c++11

我最近将GCC升级到8.2,我的大多数SFINAE表达式都停止了工作.

以下是一些简化,但演示了问题:

#include <iostream>
#include <type_traits>

class Class {
public:
    template <
        typename U,
        typename std::enable_if<
            std::is_const<typename std::remove_reference<U>::type>::value, int
        >::type...
    >
    void test() {
        std::cout << "Constant" << std::endl;
    }

    template <
        typename U,
        typename std::enable_if<
            !std::is_const<typename std::remove_reference<U>::type>::value, int
        >::type...
    >
    void test() {
        std::cout << "Mutable" << std::endl;
    }
};

int main() {
    Class c;
    c.test<int &>();
    c.test<int const &>();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

C++(gcc) - 在线试用

C++(clang) - 在线尝试

较旧版本的GCC(遗憾的是我不记得我之前安装的确切版本)以及Clang编译上面的代码就好了,但是GCC 8.2给出了一个错误说明:

 : In function 'int main()':
:29:19: error: call of overloaded 'test()' is ambiguous
     c.test();
                   ^
:12:10: note: candidate: 'void Class::test() [with U = int&; typename std::enable_if::type>::value>::type ... = {}]'
     void test() {
          ^~~~
:22:10: note: candidate: 'void Class::test() [with U = int&; typename std::enable_if::type>::value)>::type ... = {}]'
     void test() {
          ^~~~
:30:25: error: call of overloaded 'test()' is ambiguous
     c.test();
                         ^
:12:10: note: candidate: 'void Class::test() [with U = const int&; typename std::enable_if::type>::value>::type ... = {}]'
     void test() {
          ^~~~
:22:10: note: candidate: 'void Class::test() [with U = const int&; typename std::enable_if::type>::value)>::type ... = {}]'
     void test() {

通常情况下,当不同的编译器和编译器版本以不同的方式处理相同的代码时,我假设我正在调用未定义的行为.标准对上述代码有什么看法?我究竟做错了什么?


注意:问题不在于解决这个问题的方法,有几个想到的.问题是为什么这不适用于GCC 8 - 标准是否未定义,或者它是编译器错误?

注意2:由于每个人都跳过默认void类型std::enable_if,我已经改变了要使用的问题int.问题仍然存在.

注3: 创建了GCC错误报告

Rak*_*111 5

这是我的看法.简而言之,铿锵是对的,而gcc则有回归.

我们根据[temp.deduct] p7:

替换发生在函数类型和模板参数声明中使用的所有类型和表达式中.[...]

这意味着无论包装是否为空,都必须进行替换.因为我们仍处于直接背景下,所以这是SFINAE能够做到的.

接下来我们知道一个variadic参数确实被认为是一个实际的模板参数; 来自[temp.variadic] p1

一个模板参数包是接受零个或多个模板参数模板参数.

[temp.param] p2说明允许哪些非类型模板参数:

非类型模板参数应具有以下之一(可选的cv限定)类型:

  • 一个文字的类型,具有强大的结构相等性([class.compare.default]),没有可变或易变的子对象,并且如果有一个默认的成员运算符<=>,那么它被声明为public,

  • 左值参考类型,

  • 包含占位符类型([dcl.spec.auto])或的类型

  • 推导类类型的占位符([dcl.type.class.deduct]).

请注意,void不适合账单,您的代码(已发布)格式错误.