Delegationg Constructors:コンストラクタの移譲
double型の実数部/虚数部を持つ複素数:dcomplexを定義します。コンストラクタは引数2つ/1つ/なしの3通り:
struct dcomplex { dcomplex(double re, double im) : real(re), imag(im) {} dcomplex(double re) : real(re), imag(0.0) {} dcomplex() : real(0.0), imag(0.0) {} double real; double imag; };
C++11ではコンストラクト時の初期化を、ほかのコンストラクタに移譲(丸投げ/横流し)できるようになりました:
struct dcomplex { dcomplex(double re, double im) : real(re), imag(im) {} dcomplex(double re) : dcomplex(re, 0.0) {} // dcomplex(double,doule) に移譲 dcomplex() : dcomplex(0.0) {} // dcomplex(double) に移譲 double real; double imag; };
コンストラクト時の初期化がややこしい場合に重宝しそうです。
Uniform Initialization:統一された初期化
ついさっきこしらえたdcomplexで変数をいくつか定義します:
dcomplex dc2(1.2, 3.4); dcomplex dc1(5.6); dcomplex dc0; // えっ!?
...キモチ悪いよね。書式を揃えてdcomplex dc0();とは書けないんですよね。「dcomlexを返す引数なしの関数dc0」を意味しちゃいますからね。
C++11では初期化に必要な引数を{}で囲む書式が導入されました:
dcomplex dc2 { 1.2, 3.4 }; dcomplex dc1 { 5.6 }; dcomplex dc0 { }; // ついでに他のも... int i { 7 }; char c[] { 'A', 'B', 'C', 'D', 'E' };
ここまでできるなら、ついでにvector<int> v { 1, 2, 3 }; と書きたいところですが、現時点では残念ながら無理なんです。というのも、{ 1. 2. 3 } は初期化リスト:initializer_list<int> と解釈されます。今回のCTPではライブラリの変更は行われていないので、initializer_list<int>を受けるコンストラクタがないのです。ライブラリのupdateまでは:
auto data { 1, 2, 3 }; vector<int> v { data.begin(), data.end() };
でガマンしてくださいな。
Variadic Template:可変長テンプレート
本CTPの最大の目玉です。テンプレート引数の個数が可変の関数/クラステンプレートが書けるようになりました。Variadic Templateをきちんと語るにはすごく長ぁいおハナシになっちゃうので、ここではさらっと概説しておきます。
まずは関数テンプレート。任意の型/個数の引数を取り、各引数の型と値とをプリントする関数printを定義してみます:
void print() {} template<typename Tcar, typename ...Tcdr> void print(Tcar head, Tcdr... tail) { std::cout << typeid(head).name() <<':' << head << std::endl; print(tail...); }
...何コレ?って感じでしょ?
print(1, 'A', 2.3, "BC")が呼ばれると、コンパイラはprint<int,char,double,const char*>とみなします。このときテンプレート引数Tcarはint,Tcdrは「char,double,const char*のパック(カタマリ)」と解釈されます。...Tcdrの...がパック(カタマリ)を意味するわけね。
関数printに引き渡された引数についてもheadが1、tailが「'A', 2.3, "BC" のパック」です。関数print内ではheadの型名と値をcoutに出力したのち print(tail...)しています。
tail...の...はパックを展開する、つまりカタマリをほぐすことを表し、print('A', 2.3, "BC")が呼び出されます。すると今度はTcarはchar,Tcdrは「doule,const char*のパック」と解釈されて以下同文、パックのナカミが1つずつ減って空になるまで再帰的に呼び出されます。といってもホントに再帰的にprint自身が呼び出されるわけではありません。関数テンプレートですからテンプレートの型/数が異なれば異なる関数となります。なのでしいて言えばテンプレート引数の異なる"同じ名前の関数"が呼び出されるわけです。
アタマのvoid print() {}はパックが空のとき「何もしない」を定義するためのものです。
このように可変長テンプレートは、再帰的な定義によって実装します。クラステンプレートも同様に再帰的な定義です。例えば複数の型の組:tupleの場合だと:
template<int N, typename ...T> struct tuple_data; template<int N> struct tuple_data<N> { }; template<int N, typename Tcar, typename ...Tcdr> struct tuple_data<N, Tcar, Tcdr...> : public tuple_data<N+1, Tcdr...> { Tcar value; }; template<typename ...T> struct tuple : public tuple_data<0, T...> { };
...なにやってんだかさっぱりワカンネーですね。この定義によってtouple<int,char,long>の場合
tuple_data<3> {} tuple_data<2,long> { long value; } tuple_data<1,char,long> { char value; } tuple_data<0,int,char,long> { int value; }
を積み重ねて(順に継承して)touple<int,char,long>が作られます。
以上、Visual C++ Compiler November 2012 CTPで追加されたC++11の新機能でした。
Variadic Templateは、強力ながら相当にややこしいカラクリです。今回はこれくらいで勘弁してください。いつかどこかできちんと解説したいと考えています。