1. ホーム
  2. c++

[解決済み] 可変個体テンプレートにおける「...」トークンのルールとは?

2023-01-17 18:35:38

質問

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つの引数だけを取る非変数テンプレート関数です。そのための構文規則は(おおよそ)何でしょうか?それはどのように一般化することができますか?

また 関数の実装では、省略記号 ( ... ) は、関心のある表現の最後にあります。テンプレートの引数リストとパラメータリストでは省略記号が真ん中になっていますが、何か理由があるのでしょうか?

どのように解決すればよいですか?

variadic テンプレートのコンテキストでは、省略記号の ... は、テンプレートパラメータパックが式の右側に現れる場合、それを展開するために使われます(この式を呼び出します パターン に現れる場合はpackの引数として使われます。 側に表示される場合は pack 引数です。

...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() 関数呼び出しで示される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 が導き出す 公的に を全ての基本クラスから派生させます。

お役に立てれば幸いです。