SFINAE与不同的编译器一起使用VoidT会导致不同的结果


10

考虑以下代码:

template <typename T> using VoidT = void;

class A {
public:
   using TEST = int;
};

class C {
public:
   using DIFFERENT = int;
};

template <typename T, typename Enable = void>
class B {
public:
   B() = delete;
};

template <typename T>
class B<T, VoidT<typename T::TEST>> {
public:
   B() = default;
};

template <typename T>
class B<T, VoidT<typename T::DIFFERENT>> {
public:
   B() = default;
};

int main() {
   B<A> a;
   B<C> b;

   return 0;
}

使用g ++-4.8.5,编译此代码会给我以下错误消息:

~/test/compile_test> g++ -std=c++11 test.cpp

test.cpp:31:7: error: redefinition of ‘class B<T, void>’

test.cpp:24:7: error: previous definition of ‘class B<T, void>’

但是,当我使用g ++-8.3(例如ideone)进行编译时,代码将被编译,并且正确处理了不同的专业领域。这是在GCC中修复的错误,还是我以某种方式调用未定义的行为(因此,编译器行为的差异是有争议的-它是未定义的)?

Answers:


9

这是在GCC中修复的错误吗?

这是标准中的缺陷。它已针对以前的标准版本进行了追溯修复,但当然只有较新的编译器版本才有此修复程序。它是CWG第1558期,并引用以下内容:

当前的17.6.7 [temp.alias]措词未指定别名模板专业化中未使用的参数的处理。例如:

  #include <iostream>

  template <class T, class...>
    using first_of = T;

  template <class T>
    first_of<void, typename T::type> f(int)
      { std::cout << "1\n"; }

  template <class T>
    void f(...)
      { std::cout << "2\n"; }

  struct X { typedef void type; };

  int main() {
    f<X>(0);
    f<int>(0);
  }

以T为int的对first_of的引用是否等效于简单的void,还是替换失败?

没有DR修复程序的编译器的解决方法是使用帮助程序:

template<typename T> struct voider { using type = void; };
template <typename T> using VoidT = typename voider<T>::type;

在类模板中可以确保替换失败。


1
追溯修复使我感到困扰。这意味着从来没有一个规范的文档来描述该语言的任何版本。
Lightness Races in Orbit

2
@LightnessRacesinOrbit-我明白你的意思了。可以希望这样的追溯性修复仅保留给不应被拒绝的有效构造,因此危害最小。
StoryTeller-Unslander Monica,

@StoryTeller确实。
Lightness Races in Orbit
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.