web-dev-qa-db-ja.com

パラメータパックはパラメータリストの最後にある必要があります...いつ、なぜですか?

後者がクラスにバインドされている場合、パラメーターパックがパラメーターリストの最後になければならない理由がわかりませんが、パラメーターリストがメンバーメソッド宣言の一部である場合、制約は緩和されます。

言い換えれば、これはコンパイルされます:

class C {
    template<typename T, typename... Args, typename S>
    void fn() { }
};

次のものはしません:

template<typename T, typename... Args, typename S>
class C { };

最初のケースが正しいと見なされ、2番目のケースが正しくないと見なされるのはなぜですか?
つまり、それが正当な構文である場合、両方の場合に当てはまるのではないでしょうか。

明確にするために、本当の問題は、次のようなクラスを定義していたことです。

template<typename T, typename... Args, typename Allocator>
class C { };

最後のタイプとしてアロケータータイプを使用していただければ幸いですが、なんとかして回避できます(とにかく、提案があれば、私よりもはるかにエレガントです!!)。
そうは言っても、エラーが発生しました:

パラメータパック「Args」は、テンプレートパラメータリストの最後にある必要があります

それで、なぜそれが受け入れられるのかを完全に理解することに興味がありましたが、そうでない場合もあります。

ここ も同様の質問ですが、問題を解決する方法を説明しているだけで、それは私には非常に明白でした。

28
skypjack

これは関数テンプレートに有効ですが、引数の推論がコンパイラがテンプレートパラメータを解決するのに役立つ場合にのみ有効です。これは、関数テンプレートの例が事実上役に立たないためです。

template<typename T, typename... Args, typename S> void fn() { }
int main() { fn<int, int, int>(); }
test.cpp: In function 'int main()':
test.cpp:2:32: error: no matching function for call to 'fn()'
 int main() { fn<int, int, int>(); }
                                ^
test.cpp:1:57: note: candidate: template<class T, class ... Args, class S> void fn()
 template<typename T, typename... Args, typename S> void fn() { }
                                                         ^
test.cpp:1:57: note:   template argument deduction/substitution failed:
test.cpp:2:32: note:   couldn't deduce template parameter 'S'
 int main() { fn<int, int, int>(); }

コンパイラーには、どのテンプレートパラメーターがパラメーターパックに属し、どのテンプレートパラメーターがSに属するかを判別する方法がありません。実際には@ T.Cとして。この方法で定義された関数テンプレートはインスタンス化できないため、実際には構文エラーである必要があると指摘しています。

より便利な関数テンプレートは次のようになります

template<typename T, typename... Args, typename S> void fn(S s) { }

現在、コンパイラは関数パラメータsをテンプレートタイプSと明確に一致させることができますが、Sという副作用があります。 alwaysが推定されます-最初のパラメータの後のすべての明示的なテンプレートパラメータはArgsに属します。

これは(プライマリ)クラステンプレートでは機能せず、パラメータは推定されず、明示的に禁止されています。

ドラフトn4567から

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf

[temp.param]/11

[...]プライマリクラステンプレートまたはエイリアステンプレートのtemplate-parameterがテンプレートパラメータパックである場合、それが最後template-parameter。[...]

(それらが推定された場合、関数テンプレートの例のようにあいまいになります)。

16
user657267

最初のものは正しくありません。コンパイラはバグがあり、診断に失敗しました。 [temp.param]/11

関数テンプレートのテンプレートパラメータパックの後に、そのテンプレートパラメータがのparameter-type-listから推測できない限り、別のテンプレートパラメータを続けてはなりません。関数テンプレートまたはデフォルトの引数(14.8.2)があります。


関数タイプT(Args...)がエンドユーザーにとって意味がある場合、これを修正する1つの方法は、代わりに部分的な特殊化を使用することです。

template<class F, class Alloc> class C; //undefined
template<class T, class... Args, class Alloc>
class C<T(Args...), Alloc> {
    // implementation
};

実際の要件によっては、アロケーターの型消去も検討する価値があるかもしれません。

11
T.C.