「……」トークンの意味は何ですか?つまり、パラメータパックの二重省略記号演算子


110

新しいC ++ 11ヘッダーのgccの現在の実装を閲覧しているときに、「......」トークンに遭遇しました。次のコードが[ideone.comを介して]正常にコンパイルされることを確認できます。

template <typename T>
struct X
{ /* ... */ };

template <typename T, typename ... U>
struct X<T(U......)> // this line is the important one
{ /* ... */ };

それで、このトークンの意味は何ですか?

編集:SOは質問タイトルの「......」を「...」にトリミングしたように見えますが、私は本当に「......」を意味していました。:)


ヒント:の...後にはが続き...ます。
Alexandre C.

5
U...後に続くようなものではありませんか...。それにもかかわらず非常に奇妙です。
edA-qa mort-ora-y 2011

1
注:これは、<functional>とにあり<type_traits>ます。常に、テンプレートパラメータ内の関数引数リストのコンテキストにあります。
Potatoswatter

タイトルにくっついてしまったのは、その間にスペースを入れたときだけでした。読者にとってそれがより明確になることを願っています。
Matthieu M.

@Matthieu M .:ありがとうございます。
Vitus

回答:


79

その奇妙さのすべてのインスタンスは、通常の単一の省略記号のケースとペアになります。

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes...)>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes......)>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes...) const>
    { typedef _Res result_type; };

  template<typename _Res, typename... _ArgTypes>
    struct _Weak_result_type_impl<_Res(_ArgTypes......) const>
    { typedef _Res result_type; };

私の推測では、二重の省略記号はと意味が似ています_ArgTypes..., ...。つまり、可変長テンプレートの展開の後にCスタイルの可変引数リストが続きます。

これがその理論をサポートするテストです…私はこれまでで最悪の疑似演算子の新しい勝者を持っていると思います。

編集:これは適合しているように見えます。§8.3.5/ 3は、パラメータリストを次のように形成する1つの方法を説明しています

パラメータ宣言リストopt ... opt

したがって、二重の省略記号は、パラメータパックで終了するパラメータ宣言リストと、それに続く別の省略記号で構成されます。

コンマはオプションです。§8.3.5/ 4は言う

構文的に正しく、「...」が抽象宣言子の一部ではない場合、「、...」は「...」と同義です。

これ抽象宣言子内にありますが、ヨハネスはパラメーター宣言内の抽象宣言子を参照していることを指摘しています。なぜ彼らが「パラメータ宣言の一部」と言わなかったのか、そしてなぜその文は単なる情報としてのメモではないのか…。

さらに、va_begin()in <cstdarg>はvarargsリストの前にパラメーターを必要とするため、f(...)C ++で特に許可されているプロトタイプは役に立ちません。C99との相互参照は、プレーンCでは違法です。したがって、これは最も奇妙です。

使用上の注意

リクエストに応じて、ここに二重省略記号のデモンストレーションがあります。

#include <cstdio>
#include <string>

template< typename T >
T const &printf_helper( T const &x )
    { return x; }

char const *printf_helper( std::string const &x )
    { return x.c_str(); }

template< typename ... Req, typename ... Given >
int wrap_printf( int (*fn)( Req... ... ), Given ... args ) {
    return fn( printf_helper( args ) ... );
}

int main() {
    wrap_printf( &std::printf, "Hello %s\n", std::string( "world!" ) );
    wrap_printf( &std::fprintf, stderr, std::string( "Error %d" ), 5 );
}

はい、そうです。T(U ...、...)もコンパイルできます。おそらく彼らはいくつかのスペースを節約したかったのです。:)
Vitus

1
しかし、それはどういう意味ですか?そして、コンパイラは_ArgTypesが終了し、いくつかの「追加の」パラメータが開始する場所をどのようにして知ることができますか?
Bo Persson

12
@Bo Perssonの:std::is_functionvalue関数はCが1を可変引数およびT(Uが...)しているためである場合も同様でなければならない、そのような機能のために一致して、あなたがこの狂気を必要としています。たとえば、int f(int、char、...)はT(U ......)と完全に一致し、T = int、U = {int、char}および "..."可変引数トークンと一致します。
Vitus

4
「これ抽象宣言子内にあります」->それらは、同じパラメーター型リストの最後のパラメーターの抽象宣言子の一部ではないことを意味します。たとえばvoid (int...)...は抽象宣言子の一部ではないintため、と同義void(int, ...)です。を記述void(T...)T、テンプレートパラメータパックで...ある場合、は抽象宣言子の一部となるため、と同等ではありませんvoid(T, ...)
ヨハネスシャウブ-litb

2
「さらに、<cstdarg>のva_begin()はvarargsリストの前にパラメーターを必要とするため、C ++で特に許可されているプロトタイプf(...)は役に立たない。」-渡された引数を知りたい場合にのみ役に立ちます。f(...)テンプレートメタプログラミングでフォールバック関数のオーバーロードとして頻繁に使用されます。この情報は必要ありません(関数が実際に呼び出されることさえありません)。

4

テンプレートバージョンでは、vs2015でコンマを区切ることが不可欠です。

    template <typename T, typename ... U>
    struct X<T(U...,...)> {};// this line is the important one

インスタンス化の例は次のとおりです。

    X<int(int...)> my_va_func;

よろしく、FM。


私もこれに気づきました、それはまだ起こります。developercommunity.visualstudio.com/content/problem/437260/…のバグレポート。
egyik

知ってよかった。これについてのスタンダードへの参照または引用はありますか?
Red.Wave

.سلامببخشیدنمیدانم– egyik 19
1

これは公開フォーラムです。あなたの考えを人々に読んでもらいましょう。PLZはプライベートメッセージのネイティブ言語を保持します。سپاس。
Red.Wave 2019年

じゃあね 私は標準の専門家ではありません-他の人が上記で詳細にカバーしていると思います。マイクロソフトの問題報告について誰かがコメントしたい場合は、優先度を上げるかもしれません。このレポートは、clangとgccがVC ++が許可していないことを許可していることを示しています。
egyik
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.