vectorsのテンプレートを詳しく見る
はじめに
競プロやってると、多次元のvectorをよく使うことがあります。非常に便利なんだけど、宣言がめんどくさい。
例えば、3次元のvectorを宣言しようとすると、
vector<vector<vector<int>>>v(10,vector<vector<int>>(10,vector<int>(10,1000)));
こうなります。
4次元はもう書く気すらなくなってしまいます。しかしvectorsのテンプレートを用いると、
auto v = vectors(10,10,10,3);
このように簡単に書くことができます。
個の引数に対して、最後の数値を初期値として次元のvectorを生成できます。
今回はこのテンプレートを詳しく見ていくことにします(勉強を兼ねて)
vectorsテンプレート
template<typename Head, typename Value> auto vectors(const Head &head, const Value &v) { return vector<Value>(head, v); } template<typename Head, typename... Tail> auto vectors(Head x, Tail... tail) { auto inner = vectors(tail...); return vector<decltype(inner)>(x, inner); }
vectorsのテンプレートは2つのvectorsという関数から成り立っています。
これを詳しく見ていきます。
可変引数テンプレート
任意のNについてvectorを生成するので、引数は毎度変化します。
これに対応するために、可変引数テンプレート*1が用いられています。
サンプルコードを見て見ましょう。
#include<bits/stdc++.h> using namespace std; template<class ...Args> void func(Args... arg){ //argの数を表示 cout << sizeof...(arg) << endl; } template<class Head, class ...args> void func2 (Head a,args... b){ cout << a << endl; cout << sizeof...(b) << endl; } signed main(){ func(1,2,3,4,5,6,7,8); func2(1,2.2,"3",'4'); /* output: 8 1 3 */ }
可変引数テンプレートでは、任意の型と任意の引数をテンプレートとして利用することができます。
上記のfunc関数では
・Argsがテンプレートパラメータパック
・argsをが関数パラメータパック
となっています。それぞれ、推論された型、引数が格納されています。
関数内でパラメータパックを用いる際は ... を用いて展開します。
func2に注目してみると、引数として渡された引数が、Head 型の a と args型のbに分割されています。
この関数を繰り返し用いると、やがてargs...の引数は一つになります。
勘がいい方はすでに気づいているかもしれませんが、これは繰り返し処理に用いることができます。
具体例を示します。
#include<bits/stdc++.h> using namespace std; template<class Head, class args> void func2 (Head a,args b){ cout << a << endl; cout << b << endl; } template<class Head, class... args> void func2 (Head a,args... b){ func2(b...); }signed main(){; func2(1,2.2,"3",'4'); /*output 3 4 */ }
複数の引数が渡された関数は、再帰的にfunc2関数を呼び、最後に引数の引数の数は2つになります。
このように、関数をオーバーロードすることで、タグディスパッチによって最適な関数が呼び出されます。
decltypeについて
上記の知識とautoがsyntax sugarであることがわかれば、ほとんど中身が理解できる…かもしれませんが、最後にdecltypeだけ説明しておきます。
decltypeとは、指定した式から型取得する機能です。
#include<bits/stdc++.h> using namespace std; signed main(){ cout << sizeof(decltype(2.2+ 3.4)) << endl; // decltypeはdouble型 cout << sizeof(decltype('a' )) << endl; //decltypeはchar 型 cout << sizeof(decltype(2+1))<<endl; //decltypeはint型 /* output 8 1 4 */ }
vectorsテンプレートでは、decltypeで取得した型とするvectorを返すことで、入れ子構造を達成しています。
*1:任意の数でテンプレートを引数をとれるテンプレートを宣言できる機能