Effective C++ 第二章
項目5: 明示的宣言よりも auto を優先する
- auto で宣言することのメリット
- 複雑な型宣言の省略
template<typename It> void f(It b, It e) { while (b != e) { typename std::iterator_traits<It>::value_type cV = *b; // auto cV = *b; } }
auto deffLess = [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2) { return *p1 < *p2; } // C++14 auto deffLess = [](const auto& p1, const auto& p2) { return *p1 < *p2; }
C++11 で登場した std::function 型でもラムダ式は表現可能だが、以下の点で auto のほうが有利
- シンタックスが簡潔
- メモリ使用量が少ない
- 実行速度が早い
型のショートカットによる実行環境依存の問題を回避できる
std::vector<int> v; unsigned sv = v.size(); // v.size() の戻り値の型は、std::vector<int>::size_type 型は、実行環境によりサイズが異なる // Windows 32bit では、32bit // Windows 64bit では、64bit // 一方 unsigned は どちらも32bit
- 型宣言のミスによる不要なオブジェクトのコピー及び未定義動作を回避可能
std::unordered_map<std::string, int> m; // std::pair 型は std::pair<const std::string, int> が正解 // 型の不一致により、一時オブジェクトが生成され、ループ内の最後の処理で破棄される for(const std::pair<std::string, int> p : m) { ... }
- ただし、auto による型宣言にも落とし穴がある
- 統一初期化子で初期化した変数の型が std::initializer_list
になる - std::vector
型のコンテナに対して operator を実行すると、std::vector ::reference が戻されることによる未定義動作の発生
- 統一初期化子で初期化した変数の型が std::initializer_list
項目6: auto が期待と異なる型を推論する場面では ETII を用いる
- std::vector
に対する operator はコンテナ要素の参照を返さない - C++ ではビットの参照を認めていない
- std::vector
の operator[] は T& を返す関数 - bool& は返すことができないため、bool& のように振る舞うオブジェクトを返す
- それが、std::vector
::reference - これは Proxy Class の一例
- Proxy Class には使用者にプロキシを意識させるものとそうでないものがある
- 意識させるもの: スマートポインタ
- 意識させないもの: std::vector
::reference - これが auto との相性が悪い (ユーザに意識させないプロキシクラスは、ライフタイムを1文以内と想定している)
- つまり、ユーザに意識させないプロキシクラスに対する auto 宣言は避けるべき
- Proxy Class には使用者にプロキシを意識させるものとそうでないものがある
- これは Proxy Class の一例
struct Widget {}; std::vector<bool> features(const Widget& w) { std::vector<bool> v = { true, false, true, false, true }; return v; } TEST(std_bool, features) { Widget w; auto priorities = features(w)[4]; EXPECT_STREQ("std::__1::__bit_reference<std::__1::vector<bool, std::__1::allocator<bool> >, true>", boost::typeindex::type_id_with_cvr<decltype(priorities)>().pretty_name().c_str()); }
- std::vector
::referece が bool& の動作を模倣する技法はたくさんある - その一例が暗黙の型変換
bool priorities = features(w)[4];
- auto 自体が問題ではなく、推論してほしい型を推論しないことが問題
- よって目的の型への推論を強制すれば良い
- それが、ETII (Explicitly typed initializer idiom)
auto priorities = std::static_cast<bool>(features(w)[4]);