2014年7月17日木曜日

【読書ノート】Effective Modern C++ - Item 2

O'Reilly Early Releaseで購入したEffective Modern C++のドラフト版の読書記録です。
※この記事はあくまで私の備忘録です。(断りなく自分の解釈や考え・感想を入れたり、理由もなく内容を省略したり、間違いや曲解もあると思います。誤りの指摘は歓迎です。)

Item 2は、Item 1を理解していればすぐ読み終わる内容でした。
ただ、auto x = {27};でxがinitializer_list型になるのは知らなかったので新鮮でした。

Item 2: autoの型推論を理解しよう

autoの型推論は、1つの例外をのぞいてテンプレートの型推定と同じ。

autoをテンプレート関数のパラメータの型だと思えば同じだと分かる。

auto x = 27; // case 3 (xはポインタでも参照でもない)、xはint
const auto cx = x; // 同上、cxはconst int
const auto& rx = x; // case 1 (rxはuniversalでないreference)、rxはconst int&
auto&& uref1 = x; // xはintでlvalueなので、uref1の型はint&
auto&& uref2 = cx; // cxはconst intでlvalueなので、uref2の型はconst int&
auto&& uref3 = 27; // 27はintでrvalueなので、uref3の型はint&&

const char name[] = "R. N. Briggs"; // nameの型はconst char[13]
auto arr1 = name; // arr1の型はconst char*
auto& arr2 = name; // arr2の型はconst char(&)[13]

void someFunc(int, double); // someFuncは関数で、型はvoid(int, double)
auto func1 = someFunc; // func1の型はvoid (*)(int, double)
auto func2 = someFunc; // func2の型はvoid (&)(int, double)

ここまではItem 1と本質的に同じ。

autoとテンプレートの型推定のただ1つの違い

以下の4つは皆int型変数を27で初期化する文。
(下の2つはC++11で導入されたuniform initialization)

int x1 = 27;
int x2(27);
int x3 = {27};
int x4{27};

しかし、autoを使うと同じでなくなる。

auto x1 = 27; // int型で、値は27
auto x2(27); // 同上
auto x3 = {27}; // std::initializer_list<int>型で、値は{27}
auto x4{27}; // 同上

以下の文はstd::initializer_list<T>のTを推定できないためコンパイルエラーになる。

auto x5 = {1, 2, 3.0};

まずx5は{}の存在によりstd::initializer_listと推定される。
次に、std::initializer_listはテンプレートなのでstd::initializer_list<T>のTが決まらなければならない。
しかし、{}内の数値型が同じではないのでTが推定できずコンパイルエラーとなる。
この{}による初期化のみがテンプレートとautoの型推定の違い。

なぜこのような仕様になっているか、説得力のあるな説明はない。ルールである。
この落とし穴のため、{}による初期化はそうしなければならない時のみに使用する開発者もいる。
({}を使わなければならない場面はItem 7に説明がある。)

C++11の範囲では話は以上だが、C++14では続きがある。
C++14ではラムダ式でautoが使える。
そしてこのautoはテンプレートの型推定を行う。({}をstd::initializer_listとは推定しない。)
従って、下記のコードは{ 1, 2, 3 }の型を推定できないのでコンパイルに失敗する。

auto createInitList()
{ return { 1, 2, 3 }; // コンパイルエラー } std::vector v; ... auto resetV = [&v](const auto& newValue) { v = newValue; } ... resetV( { 1, 2, 3 } ); // コンパイルエラー

Things to Remember

  • autoの型推定は通常テンプレートの型推定と同じ
  • 唯一の例外はautoと{}による初期化を使用した変数宣言で、そこでautoはstd::initializer_list型を推定する
  • テンプレートの型推定は{}による初期化リストを与えられると失敗する

0 件のコメント:

コメントを投稿