Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Emcpp0506

8,352 views

Published on

A presentation at the Effective Modern C++ study group in Japan.

Published in: Software
  • Be the first to comment

Emcpp0506

  1. 1. Effective Modern C++ 勉強会 Item5, 6 近藤 貴俊 2015/1/31 1
  2. 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. 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. 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. 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. 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. 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. 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. 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. 10. Item 5: Prefer auto to explicit type declarations. 2015/1/31 10 Things to Remember • auto型変数は初期化が必須である。 また、型のミスマッチによる移植性や効率の問題を防ぐことができる。 さらに、リファクタリングを容易にし、 一般的に明示的な型宣言による変数定義に比べて タイピング量が少なくて済む。 • auto型変数は Item2および6で述べる落とし穴の影響を受ける点に注意
  11. 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. 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. 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. 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. 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させることができる。

×