17

最初にいくつかのコード、次にいくつかのコンテキスト、次に質問:

template <typename T> using id = T;

template <template <typename...> class F, typename... T>
using apply1 = F <T...>;

template <template <typename...> class F>
struct apply2
{
    template <typename... T>
    using map = F <T...>;
};

// ...

cout << apply1 <id, int>() << endl;
cout << apply2 <id>::map <int>() << endl;

clang 3.3 と gcc 4.8.1 の両方がこれをエラーなしでコンパイルし、恒等メタ関数を に適用するintため、両方の式がデフォルトint(ゼロ) に評価されます。

idそれがtemplate <typename>しばらくの間apply1であるという事実は、そもそも私が心配していたことをapply2期待しています。ただし、この例が機能することは非常にtemplate <typename...>便利です。apply1apply2

一方、このようなテンプレート エイリアスは、ここでは再現できない実際のコードで深刻な問題を引き起こします。gcc では頻繁に内部コンパイラ エラーが発生し、clang ではあまり頻繁に予期しない動作が発生します (より高度な SFINAE テストでのみ)。

数か月の試行錯誤の後、(実験的な) gcc 4.9.0 にコードをインストールして試してみると、次のエラーが表示されます。

test.cpp: In instantiation of ‘struct apply2<id>’:
test.cpp:17:22: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using id = T’
  using map = F <T...>; 
                      ^

さて、このコードはずっと有効ではなかったようですが、gcc はエラーを報告する代わりにさまざまな方法でクラッシュしました。興味深いことに、は同等apply1apply2見えますが、エラーが報告されるのはapply2(実際にははるかに便利です) だけです。クランに関しては、私は本当に言うことができません。

実際には、gcc 4.9.0 に合わせてコードを修正する以外に方法はないようです。

理論的には、標準が何を言っているのか知りたいです: このコードは有効ですか? そうでない場合、 の使用もapply1無効ですか? またはのみapply2

編集

これまでに発生したすべての問題は、テンプレートの構造体ではなく、テンプレートのエイリアスを参照していることを明確にするために。たとえば、次の変更を検討してください。

template <typename T> struct id1 { using type = T; };

// ...

cout << typename apply1 <id1, int>::type() << endl;
cout << typename apply2 <id1>::map <int>::type() << endl;

これ0は、clang 3.3、gcc 4.8.1、gcc 4.9.0 の両方の場合で、正常にコンパイルされ、印刷されます。

ほとんどの場合、私の回避策はエイリアスの前に中間テンプレート構造体を導入しています。ただし、メタ関数を使用して一般的な SFINAE テストをパラメーター化しようとしています。この場合、構造体をインスタンス化してはならないため、エイリアスを直接使用する必要があります。アイデアを得るために、実際のコードの一部をここに示します。

4

1 に答える 1