SlideShare a Scribd company logo
Effective Modern C++ 勉強会
Item5, 6
近藤 貴俊
2015/1/31 1
今回紹介する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 e
while (b != e) {
typename std::iterator_traits<It>::value_type
currValue = *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 e
while (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 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
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を使う
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と呼ぶ
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));
こうすれば意図が明確に伝わる
こうすれば意図が明確に伝わる
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させることができる。

More Related Content

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させることができる。