可変長テンプレートのコンテキストでの「…」トークンのルールは何ですか?


98

C ++ 11には、次のような可変テンプレートがあります。

template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args )
{
    return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

このことについて、いくつかの好奇心があります。式は、std::forward<Args>(args)...両方を使用Argsし、args一つだけ...のトークン。さらにstd::forward、1つのテンプレートパラメーターと1つの引数のみを取る非可変テンプレート関数があります。(大まかに)そのための構文規則は何ですか?どのように一般化できますか?

また、関数の実装では、省略記号(...)は対象の式の最後にあります。テンプレート引数リストとパラメーターリストで省略記号が中央にある理由はありますか?


2
第2部について簡単に説明します。テンプレートパラメータパックまたは関数パラメータパックを「宣言」するとき...、識別子が導入される前にが来ます。どちらか一方または両方のタイプのパックを使用する場合、...は展開する表現パターンの後に来ます。
aschepler 2013

回答:


99

可変個のテンプレートのコンテキストで...は、テンプレートのパラメーターパックが式の右側に表示されている場合は、省略記号を使用してテンプレートパラメーターパックを展開します(この式パターンを少しの間呼び出します)。ルールは、の左側にあるパターンは何でも...繰り返されるということです。アンパックされたパターン(ここではと呼びます)はコンマで区切られます,

いくつかの例で最もよく理解できます。次の関数テンプレートがあるとします。

template<typename ...T>
void f(T ... args) 
{
   g( args... );        //pattern = args
   h( x(args)... );     //pattern = x(args)
   m( y(args...) );     //pattern = args (as argument to y())
   n( z<T>(args)... );  //pattern = z<T>(args)
}

この関数をTとして渡して呼び出す{int, char, short}と、各関数呼び出しは次のように展開されます。

g( arg0, arg1, arg2 );           
h( x(arg0), x(arg1), x(arg2) );
m( y(arg0, arg1, arg2) );
n( z<int>(arg0), z<char>(arg1), z<short>(arg2) );

投稿したコードでstd::forwardn()関数呼び出しによって示されている4番目のパターンに従います。

x(args)...y(args...)上記の違いに注意してください!


を使用...して配列を初期化することもできます。

struct data_info
{
     boost::any  data;
     std::size_t type_size;
};

std::vector<data_info> v{{args, sizeof(T)}...}; //pattern = {args, sizeof(T)}

これに拡張されます:

std::vector<data_info> v 
{ 
   {arg0, sizeof(int)},
   {arg1, sizeof(char)},
   {arg2, sizeof(short)}
};

私はパターンが次のようなアクセス指定子を含むことさえできることに気づきました public次の例に示すように、。

template<typename ... Mixins>
struct mixture : public Mixins ...  //pattern = public Mixins
{
    //code
};

この例では、パターンは次のように展開されます。

struct mixture__instantiated : public Mixin0, public Mixin1, .. public MixinN  

あれは、 mixtureすべての基本クラスからパブリック派生します。

お役に立てば幸いです。


1
一致する表現について; それは可能な限り最大の表現であるべきです、そうではありませんか?たとえば、x+args...に拡張する必要がx+arg0,x+arg1,x+arg2ありx+arg0,arg1,arg2ます。
ビットマスク2013

したがって、...はパターン内のすべての展開可能なエンティティに適用されます。
2013

@ビットマスク:はい。x+args...拡大しなければならないx+arg0,x+arg1,x+arg2ではありません x+arg0,arg1,arg2
Nawaz 2013

1
@ Jarod42:C ++ 17がリリースされたら、この回答を更新します。
Nawaz、2015年

3
@synther:sizeof...(T)必要ありません。あなたは単に書くことができます:int a[] = { ___ };
Nawaz '28

48

以下は、GoingNative 2012でのAndrei Alexandrescuによる講演「Variadic Templates are Funadic」からの引用です。可変テンプレートの良い入門書としてお勧めします。


Variadicパックでできることは2つあります。応募可能ですsizeof...(vs)要素の数を取得して拡張するためするです。

拡張ルール

Use            Expansion

Ts...          T1, ..., Tn
Ts&&...        T1&&, ..., Tn&&
x<Ts,Y>::z...  x<T1,Y>::z, ..., x<Tn,Y>::z
x<Ts&,Us>...   x<T1&,U1>, ..., x<Tn&,Un>
func(5,vs)...  func(5,v1), ..., func(5,vn)

拡張は内側に向かって外側に進みます。ロックステップで2つのリストを展開する場合、それらは同じサイズでなければなりません。

その他の例:

gun(A<Ts...>::hun(vs)...);

Tsのテンプレート引数リストのすべてを展開するAと、関数hunはallで展開されますvs

gun(A<Ts...>::hun(vs...));

すべての拡大Tsのテンプレート引数リスト内Aとすべてvsのための関数の引数としてhun

gun(A<Ts>::hun(vs)...);

ロックステップ機能hun付きTsおよびvsロックステップ機能を拡張します。

注意:

Tsはタイプでvsはなく、値でもありません!これらは、タイプ/値のリストのエイリアスです。どちらのリストも空になる可能性があります。どちらも特定のアクションのみに従います。したがって、以下は不可能です。

typedef Ts MyList;  // error!
Ts var;             // error!
auto copy = vs;     // error!

拡張軌跡

関数の引数

template <typename... Ts>
void fun(Ts... vs)

初期化リスト

any a[] = { vs... };

基本指定子

template <typename... Ts>
struct C : Ts... {};
template <typename... Ts>
struct D : Box<Ts>... { /**/ };

メンバー初期化子リスト

// Inside struct D
template <typename... Us>
D(Us... vs) : Box<Ts>(vs)... {}

テンプレートの引数リスト

std::map<Ts...> m;

引数が一致する可能性がある場合にのみコンパイルされます。

キャプチャリスト

template <class... Ts> void fun(Ts... vs) {
    auto g = [&vs...] { return gun(vs...); }
    g();
}

属性リスト

struct [[ Ts... ]] IAmFromTheFuture {};

仕様書にはありますが、型として表現できる属性はまだありません。


いいね。ただし、関数の引数は軌跡から除外されます。:P
Potatoswatter '15

@Potatoswatterありがとう。関数の引数リストの展開については言及されていません。
typ1232 2013

@ typ1232編集は申し訳ありませんが、元の投稿が盗用に近すぎると感じました。ところで、私はその話も少し前に見ました-すごい。
Walter、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.