最初にいくつかのコード、次にいくつかのコンテキスト、次に質問:
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...>
便利です。apply1
apply2
一方、このようなテンプレート エイリアスは、ここでは再現できない実際のコードで深刻な問題を引き起こします。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 はエラーを報告する代わりにさまざまな方法でクラッシュしました。興味深いことに、は同等apply1
にapply2
見えますが、エラーが報告されるのは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 テストをパラメーター化しようとしています。この場合、構造体をインスタンス化してはならないため、エイリアスを直接使用する必要があります。アイデアを得るために、実際のコードの一部をここに示します。