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
の両方を使用しますが、1つの_...
_トークンのみを使用します。さらに、_std::forward
_は、1つのテンプレートパラメーターと1つの引数のみを取る非可変テンプレート関数です。 (大まかに)そのための構文規則は何ですか?どのように一般化できますか?
また、関数の実装では、省略記号(_...
_)が対象の式の末尾にあります。テンプレート引数リストとパラメーターリストで省略記号が真ん中にある理由はありますか?
可変テンプレートのコンテキストでは、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を派生させます。
お役に立てば幸いです。
以下は、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)...);
関数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 {};
仕様書にはありますが、型として表現できる属性はまだありません。