47

pack 引数は、エイリアス テンプレートの pack パラメータの場所でのみ展開できるようです。これは、クラスまたは関数テンプレートには当てはまりません。

template <class T, class... Args> struct x { using type = T; };

template <class T, class... Args> using x_t     = typename x<T, Args...>::type;
template <class... Args>          using x_fix_t = typename x<Args...>::type;

template <class... Args> auto f(Args...) -> void {
  typename x<Args...>::type v1; // OK
  x_t<Args...> v2; // Error
  x_fix_t<Args...> v3; // OK
}

より単純なケース:

template <class T, class U> using y_t = T;

template <class... Args> auto f(Args...) -> void {
  y_t<Args...> v4; // Error
}

上記のコードは、とin 、とfの両方で ( がインスタンス化されていなくても)エラーを生成します。c++11c++14g++ 4.9g++ 5.1clang 3.5

これが許可されない理由と一般的なルールは何ですか? これを制限する理由はありません。それは非常に奇妙な禁止のようです。

x_fix_t最初のバリアントのように書かない理由についてx_tは、必須の最初の引数があることがより明確です。(たとえば、それがf()許可されていない理由です)。しかし、これはそれほど重要ではありません。修正は簡単です。疑問が残ります:なぜですか?

gcc エラー:

error: pack expansion argument for non-pack parameter ‘T’ of
alias template ‘template<class T, class ... Args> using x_t = typename x::type’

クランエラー:

error: pack expansion used as argument for non-pack parameter of
alias template   x_t<Args...> v2;
4

1 に答える 1

28

これは GCC 4.8 ではコンパイルできますが、GCC 4.9 では失敗します。これは、CWG 1430 およびバグ レポート #59498に関連している証拠です。Roy Chrihfield によって提案された修正は、あなたのものとまったく同じです。

Rewriting the code to use a struct succeeds:

template <typename T, typename ...>
struct alias { using type = T; };

template <typename ...T>
using variadic_alias = typename alias<T...>::type;

さらに、Jason Merrill はなぜ失敗するのかを詳しく説明しています。

実際、いいえ、これは Core 1430 の問題です。マングリングでエイリアス テンプレートの名前に言及せずに variadic_alias<T...> をマングルする方法はなく、それらは完全に透過的であるはずです。リリースではチェックが無効になっているため、これは偶然 4.8 でのみ機能します。

バグ レポートにはこれ以上の議論がないため、CWG 1430 に目を向けることができます。

元々、パック展開は固定長のテンプレート パラメータ リストに展開できませんでしたが、これは N2555 で変更されました。これはほとんどのテンプレートで問題なく機能しますが、エイリアス テンプレートで問題が発生します。

ほとんどの場合、エイリアス テンプレートは透過的です。テンプレートで使用する場合は、依存するテンプレート引数で置き換えることができます。ただし、template-id が非可変引数のパック展開を使用している場合、これは機能しません。例えば:

template<class T, class U, class V>
struct S {};

template<class T, class V>
using A = S<T, int, V>;

template<class... Ts>
void foo(A<Ts...>);

A<Ts...> を S で表現する方法はないため、置換する Ts が得られるまで A を保持する必要があるため、マングリングで処理する必要があります。

現在、EDG と Clang はこのテストケースを拒否し、A のテンプレート引数が少なすぎることを訴えています。G++ も同様でしたが、これはバグだと思いました。しかし、ABI リストでは、John Spicer は拒否されるべきだと主張しました。

(問題 1558 も参照してください。)

2012 年 10 月の会議のメモ:

CWG のコンセンサスは、従属引数を type-id に単純に直接代入できない場合、エイリアス テンプレートの使用を許可せず、この使用法を禁止する必要があるというものでした。

追記、2013 年 4 月:

別の例として、次のことを考慮してください。

  template<class... x> class list{};
  template<class a, class... b> using tail=list<b...>;
  template <class...T> void f(tail<T...>);

  int main() {
    f<int,int>({});
  }

この例の処理には実装の違いがあります。

言い換えれば、これは解決策 (AFAIC) が見えない進行中の問題です。

于 2015-10-14T16:40:24.650 に答える