emcpp0506

15
Effective Modern C++ 勉強会 Item5, 6 近藤 貴俊 2015/1/31 1

Upload: takatoshi-kondo

Post on 22-Jul-2015

160 views

Category:

Software


0 download

TRANSCRIPT

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 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));

こうすれば意図が明確に伝わる

こうすれば意図が明確に伝わる

Item 6: Use the explicitly typed initializer idiomwhen auto deduces undesired types.

2015/1/31 15

autoが期待通りに推論してくれない場合は、明示的に型付けされた初期化イディオムを使おう

Things to Remember

• invisibleなproxy型は、auto型の変数で初期化すると、autoが不適切な型としてdeduceされる。

• explicitly typed initializer idiomを用いることで、autoを期待通りの型としてdeduceさせることができる。