C++0xのautoの紹介
autoキーワードを使って変数を定義すると,コンパイラが初期化値を基に変数の型を推論してくれる.
int f(); auto x1 = 1; // x1 : int auto x2 = f(); // x2 : int auto x3 = 3.14; // x3 : double auto &x4 = x1; // x4 : int& const auto *x5 = &x1; // x5 : const int*
イテレータのように長い名前の型もautoを使えば簡単に書ける.
// before for (std::vector<int>iterator it = v.begin(), end = v.end(); it != end; ++it) { ... } // after for (auto it = v.begin(), end = v.end(); it != end; ++it) { ... }
以下のような複雑なアレもこんな風に簡単にアレできる.
// before template<class Iterator> void f(Iterator it) { typename std::iterator_traits<Iterator>::value_type temp = *it; ... } // after template<class Iterator> void f(Iterator it) { auto temp = *it; ... }
ラムダ関数も簡単に代入できる.
auto f = [](int n){ std::cout << n << std::endl; }; std::vector<int> v = {1, 2, 3, 4, 5}; // C++0xの初期化リスト std::for_each(v.begin(), v.end(), f);
autoが使えないケース
autoで変数を定義する時は必ず初期化が必要.
auto a; // NG.初期化されないと型を特定できない.
関数の引数や戻り値の型はautoにはできない.この場合はテンプレートを使おう.
void f(auto x); // NG.引数の型はautoにできない auto g(); // NG.戻り値の型はautoにできない
autoとポインタと参照
ポインタや参照が絡んできた時の型推論についてまとめてみる.
まずはポインタ.autoによって推論された型がポインタとなる場合は,autoの横に明示的に「*」を記述した場合,または初期化値の型がポインタの場合.
A a; auto x1 = a; // x1 : A auto x2 = &a; // x2 : A* auto *x3 = &a; // x3 : A* auto *x4 = a; // エラー.初期化値がポインタ型ではない A f(); auto y1 = f(); // y1 : A auto *y2 = f(); // エラー.初期化値がポインタ型ではない A* g(); auto z1 = g(); // z1 : A* auto *z2 = g(); // z2 : A*
次は参照.autoによって推論された型が参照となる場合は,autoの横に明示的に「&」を記述した場合のみ.
A a; auto x1 = a; // x1 : A auto &x2 = a; // x2 : A& A f(); auto y1 = f(); // y1 : A auto &y2 = f(); // エラー.右辺値(一時オブジェクト)に対して左辺値参照変数を定義することはできない A& g(); auto z1 = g(); // z1 : A auto &z2 = g(); // z2 : A&
autoと基底クラスのポインタで発生する問題
基底クラスのポインタ経由で派生クラスのオブジェクトを参照し,これを用いてautoで定義した変数を初期化しようとすると問題が発生することがある.
class B { public: virtual ~B(){} }; class D : public B {}; D *pd = new D(); auto x1 = pd; // x1 : D* auto x2 = *pd; // x2 : D B *pb = new D(); auto y1 = pb; // y1 : B* auto y2 = *pb; // y2 : B (DからBへのスライシングが発生している?)
上のコードをg++ 4.6で実行すると,pbはD型のオブジェクトを指しているにもかかわらず,y2はB型のオブジェクトとなる(typeid(y2) == typeid(B)が真になる).おそらくD型のオブジェクトである*pbのうちB型と共通する部分のみがy2に中途半端にコピーされ,D型の部分のメモリはコピーされず,y2は不完全なオブジェクトとなってしまっているのだろうと考えられる.autoによる型推論は(コンパイル時に決定可能な)静的な型によって行われ,ポインタの指している先のオブジェクトの型までは考慮されないみたいなので注意が必要だ.