emcpp0506
TRANSCRIPT
今回紹介するItem
• Item 5: Prefer auto to explicit type declarations.• Item 6: Use the explicitly typed initializer idiom when
auto deduces undesired types.
2015/1/31 2
Item 5: Prefer auto to explicit type declarations.
int x;
2015/1/31 3
xが未初期化
明示的な型宣言よりもautoを使おう
auto x; コンパイルエラーにできる
auto x = 0; 初期化を必須にできる
template<typename It> // algorithm to dwim ("do what I mean")void dwim(It b, It e) // for all elements in range from{ // b to ewhile (b != e) {typename std::iterator_traits<It>::value_typecurrValue = *b;
}}
自明なことを長々と書かねばならなかった
Item 5: Prefer auto to explicit type declarations.
int x;
2015/1/31 4
xが未初期化
明示的な型宣言よりもautoを使おう
auto x; コンパイルエラーにできる
auto x = 0; 初期化を必須にできる
template<typename It> // algorithm to dwim ("do what I mean")void dwim(It b, It e) // for all elements in range from{ // b to ewhile (b != e) {auto currValue = *b;
}}
autoですっきり
C++14からはlambda expressionの引数もautoで推論可能
Item 5: Prefer auto to explicit type declarations.
2015/1/31 5
明示的な型宣言よりもautoを使おう
auto derefUPLess = // comparison func.[](const auto& p1, // for Widgets
const auto& p2) // pointed to by{ return *p1 < *p2; }; // std::unique_ptrs
lambda expressionもautoで受けることができる
auto derefUPLess = // comparison func.[](const std::unique_ptr<Widget>& p1, // for Widgets
const std::unique_ptr<Widget>& p2) // pointed to by{ return *p1 < *p2; }; // std::unique_ptrs
std::functionとの違いは?
std::functionを使うと明示的な型指定が必要となる
Item 5: Prefer auto to explicit type declarations.
2015/1/31 6
明示的な型宣言よりもautoを使おう
std::function<bool(const std::unique_ptr<Widget>&,const std::unique_ptr<Widget>&)>
derefUPLess = [](const std::unique_ptr<Widget>& p1,const std::unique_ptr<Widget>& p2){ return *p1 < *p2; };
C++14ならば、lambda expressionの引数だけautoにするのはどうか?Scottにメールしてみたところ、上記のようにautoを使うことを推奨していた。
std::function<bool(const std::unique_ptr<Widget>&,const std::unique_ptr<Widget>&)>
derefUPLess = [](const auto& p1,const auto& p2){ return *p1 < *p2; };
Item 5: Prefer auto to explicit type declarations.
2015/1/31 7
明示的な型宣言よりもautoを使おう
• std::functionとautoでlambda expressionを受ける場合の違い– autoで受ける場合、autoの型はclosureの型と同じとなる
• メモリもclosureの要求するサイズとなる
– std::functionは、どんなsignatureが渡されても固定サイズを持つ• そのサイズは、closureの要求するメモリサイズよりも小さいかも知れない
• その場合、std::functionのコンストラクタはclosureを保存するためにヒープからメモリを確保する
– std::functionはautoの場合に比べて一般的に多くのメモリを使用する
– std::functionの呼び出しはinline化されず、間接的な呼び出しとなる• autoに比べて呼び出しが遅くなるのはほぼ間違いない
– std::functionはメモリ不足の例外を投げるかもしれない
v.size()の戻り値の型は std::vector<int>::size_typeであるunsignedの型はunsigned intであるこれらの型はプラットフォームにより異なる
Item 5: Prefer auto to explicit type declarations.
2015/1/31 8
明示的な型宣言よりもautoを使おう
std::vector<int> v;…unsigned sz = v.size();
auto sz = v.size(); // sz's type is std::vector<int>::size_type
autoにすれば、型は常にstd::vector<int>::size_typeとなる
std::size_tについても考察
autoにすることで解決
std::unordered_map<std::string, int>::value_typeはstd::pair<const std::string, int> であるよって、一時オブジェクトが生成されてしまう
Item 5: Prefer auto to explicit type declarations.
2015/1/31 9
明示的な型宣言よりもautoを使おう
std::unordered_map<std::string, int> m;…for (const std::pair<std::string, int>& p : m){… // do something with p
}
std::unordered_map<std::string, int> m;…for (const auto& p : m){… // do something with p
}
Item 5: Prefer auto to explicit type declarations.
2015/1/31 10
Things to Remember
• auto型変数は初期化が必須である。また、型のミスマッチによる移植性や効率の問題を防ぐことができる。さらに、リファクタリングを容易にし、一般的に明示的な型宣言による変数定義に比べてタイピング量が少なくて済む。
• auto型変数は Item2および6で述べる落とし穴の影響を受ける点に注意
もしboolをautoに変更したら、undefined behaviorになるstd::vector<bool>のoperator[]は、boolではなく、std::vector<bool>::referenceを返すため。std::vector<bool>は効率化のため、1byte内にbitをpackする。C++はbit単位でのreferenceを返すことができない。auto&でstd::vector<bool>::referenceを受けると、operator[]で生成された一時オブジェクトを参照してしまう。それをprocessWidgetに渡すため、undefined behaviorになる。
Item 6: Use the explicitly typed initializer idiomwhen auto deduces undesired types.
2015/1/31 11
autoが期待通りに推論してくれない場合は、明示的に型付けされた初期化イディオムを使おう
5bit目がtrueならばhigh priorityを示すとする
std::vector<bool> features(const Widget& w);
Widget w;…bool highPriority = features(w)[5]; // is w high priority?…processWidget(w, highPriority); // process w in accord
// with its priority
Item 6: Use the explicitly typed initializer idiomwhen auto deduces undesired types.
2015/1/31 12
autoが期待通りに推論してくれない場合は、明示的に型付けされた初期化イディオムを使おう
Matrixの演算がexpression templatesで実現されている場合、autoで受けても演算が実行されない。その代わりに、sumは Sum<Sum<Sum<Matrix, Matrix>,Matrix>, Matrix> のような構文木の型を持つオブジェクトとなってしまう。
Matrix sum = m1 + m2 + m3 + m4;
NOTE前述のstd::vector<bool>のoperator[]や、Matrixのoperator+は、proxy classのオブジェクトを返している。そして、明示的に型指定されたオブジェクトの初期化の際に、実体を導出している。
shared_ptrやunique_ptrなどは、その名前からproxy classであることが読み取れるが、std::vector<bool>::referenceはそうではない。これらはinvisibleなproxy classといえる。
これらを見分けるためのヒントとして、T型コンテナのメンバ関数がT&を返すことが期待される場合に、異なる型(例えばクラス内にネストして定義された型)を返している場合、proxy classの可能性を考えてみよう。
このようなケースでは、次ページのようにautoを使う
Item 6: Use the explicitly typed initializer idiomwhen auto deduces undesired types.
2015/1/31 13
autoが期待通りに推論してくれない場合は、明示的に型付けされた初期化イディオムを使おう
bool highPriority = features(w)[5];
auto highPriority = static_cast<bool>(features(w)[5]);
Matrix sum = m1 + m2 + m3 + m4;
auto sum = static_cast<Matrix>(m1 + m2 + m3 + m4);
右辺でcastすることで、proxy classオブジェクトから実体を取り出す
そこまでしてautoを使うのか??と考えるならば次のページの例を見てみよう
このように右辺値のcastとautoの組み合わせを、explicitly typed initializer idiomと呼ぶ
d は double型で 0.0~1.0の間の値で、indexの先頭からの位置を示すとする
Item 6: Use the explicitly typed initializer idiomwhen auto deduces undesired types.
2015/1/31 14
autoが期待通りに推論してくれない場合は、明示的に型付けされた初期化イディオムを使おう
double calcEpsilon(); // return tolerance value
float ep = calcEpsilon(); // implicitly convert// double → float
意図してdouble から floatに変換しているのか、ミスか分かりにくい
auto ep = static_cast<float>(calcEpsilon());
int index = d * (c.size() - 1);
auto index = static_cast<int>(d * (c.size() - 1));
こうすれば意図が明確に伝わる
こうすれば意図が明確に伝わる