web-dev-qa-db-ja.com

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

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)...Argsargsの両方を使用しますが、1つの_..._トークンのみを使用します。さらに、_std::forward_は、1つのテンプレートパラメーターと1つの引数のみを取る非可変テンプレート関数です。 (大まかに)そのための構文規則は何ですか?どのように一般化できますか?

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

97
Ralph Tandetzky

可変テンプレートのコンテキストでは、Ellipsis _..._を使用して、テンプレートパラメーターパックが式の右側に表示されている場合(この式を呼び出すpatternしばらくの間)。ルールは、_..._の左側にあるすべてのpatternが繰り返されるということです—パックされていないパターン(それらを呼び出すexpressionsnow)はコンマ_,_で区切られます。

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

_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::forward_はn()関数呼び出しで示されている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は、すべての基本クラスからpubliclyを派生させます。

お役に立てば幸いです。

97
Nawaz

以下は、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をすべてのvsで展開します。

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

Tsのテンプレート引数リスト内のすべてのAを展開し、vsの関数引数としてすべてのhunを展開します。

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

関数hunTsおよび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 {};

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

48
typ1232