Publicidad

Emcpp0506

Software Engineer at OGIS-RI en OGIS-RI
31 de Jan de 2015
Publicidad

Más contenido relacionado

Publicidad

Último(20)

Emcpp0506

  1. Effective Modern C++ 勉強会 Item5, 6 近藤 貴俊 2015/1/31 1
  2. 今回紹介する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
  3. 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 e while (b != e) { typename std::iterator_traits<It>::value_type currValue = *b; } } 自明なことを長々と書かねばならなかった
  4. 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 e while (b != e) { auto currValue = *b; } } autoですっきり
  5. 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との違いは?
  6. 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; };
  7. 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はメモリ不足の例外を投げるかもしれない
  8. 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についても考察
  9. 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 }
  10. Item 5: Prefer auto to explicit type declarations. 2015/1/31 10 Things to Remember • auto型変数は初期化が必須である。 また、型のミスマッチによる移植性や効率の問題を防ぐことができる。 さらに、リファクタリングを容易にし、 一般的に明示的な型宣言による変数定義に比べて タイピング量が少なくて済む。 • auto型変数は Item2および6で述べる落とし穴の影響を受ける点に注意
  11. もし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 idiom when 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
  12. Item 6: Use the explicitly typed initializer idiom when 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を使う
  13. Item 6: Use the explicitly typed initializer idiom when 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と呼ぶ
  14. d は double型で 0.0~1.0の間の値で、indexの先頭からの位置を示すとする Item 6: Use the explicitly typed initializer idiom when 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)); こうすれば意図が明確に伝わる こうすれば意図が明確に伝わる
  15. Item 6: Use the explicitly typed initializer idiom when auto deduces undesired types. 2015/1/31 15 autoが期待通りに推論してくれない場合は、 明示的に型付けされた初期化イディオムを使おう Things to Remember • invisibleなproxy型は、auto型の変数で初期化すると、 autoが不適切な型としてdeduceされる。 • explicitly typed initializer idiomを用いることで、autoを期待通りの型として deduceさせることができる。
Publicidad