101

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

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

4

2 に答える 2

103

可変個引数テンプレートのコンテキストでは、省略記号...は、式の右側に表示される場合はテンプレート パラメーター パックをアンパックするために使用されます (この式パターンを少し呼び出します)。または、それが式の左側に表示される場合はパック引数です。名前:

...thing  // pack   : appears as template arguments
thing...  // unpack : appears when consuming the arguments

ルールは、 の左側にあるパターン...が繰り返されるということです — アンパックされたパターン (ここではと呼びます) はコンマで区切られます,

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

template<typename ...T> //pack
void f(T ... args)      //pack
{
   // here are unpack patterns

   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示されている 4 番目のパターンに従います。n()

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に派生します。

それが役立つことを願っています。

于 2013-07-15T11:00:53.723 に答える
49

以下は、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のテンプレート引数リストでall を展開しA、次に関数hunを all で展開しますvs

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

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

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

とロックステップで機能hunを拡張します。Tsvs

ノート:

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 {};

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

于 2013-07-15T11:46:46.037 に答える