異なるコンパイラーでVoidTを使用するSFINAEは異なる結果をもたらします


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のバグですか、それとも何らかの理由で未定義の動作を呼び出していますか(したがって、コンパイラの動作の違いは問題点です-未定義です)?

回答:


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
遡及修正は私を困らせます。これは、言語のバージョンを説明する正規のドキュメントが存在しないことを意味します。
Orbitのライトネスレース、

2
@LightnessRacesinOrbit-私はあなたの意見を理解しています。そのような遡及的修正が拒否されてはならない有効な構成要素に対してのみ予約されることを期待できます。したがって、害は最小限に抑えられます。
StoryTeller-Unslander Monica

@StoryTeller確かに。
オービットのライトネスレース
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.