Effective C++ 第三章 その1
項目7: オブジェクト作成時の {} と ()の違い
- 初期値の設定方法は以下の3つ
- {}
- =
- ()
- ユーザ定義型では、初期化構文によって呼び出される関数が異なる
Widget w1; // デフォルトコンストラクタを呼び出す Widget w2 = w1; // コピーコンストラクタを呼び出す w1 = w2; //コピー演算子=を呼び出す
C+11 から初期化の統一記法が導入された
- {} による初期化
- コンテナの初期要素の指定も可能に
- 非スタティックなメンバ変数のデフォルト値の設定にも利用可能
- コピー不可能なオブジェクト(std::atomic など) では、= による初期化はできない
- 精度が落ちる変換を認めない
- 最も厄介な構文解析の回避
{} による初期化の注意点
std::vector も std::initalizer_list 型の仮引数を持つコンストラクタを実装しているため注意
- テンプレート内のオブジェクト作成に () か {} を使うかは慎重に選択する必要がある
項目8: 0やNULL よりも nullptr を優先する
- nullptr はすべてのポインタ型を表す型 (std::nullptr_t)
- テンプレートが型推論するとき、0 や NULL を誤った型に推論するため、nullptr を選択する
項目9: typedef よりもエイリアス宣言を優先する
- まず、関数型宣言がエイリアス宣言のほうがわかりやすい
typedef void (*FP1) (int, const std::string&); using FP2 = void (*) Iint, const std::string&);
// typdef template<typename T> struct MyAllocList1 { typedef std::list<T, std::allocator<T>> type; }; template<typename T> class Widget2 { private: typename MyAllocList1<T>::type list; }; //型エイリアス template<typename T> using MyAllocList2 = std::list<T, std::allocator<T>>; template<typename T> class Widget3 { private: MyAllocList2<T> list; };
項目10: enum にはスコープを設ける
// C++98 方式で enum を定義した場合、enumの列挙子名はenumを定義したスコープに含まれるため // 以下のように実装するとコンパイルエラーとなる //enum Color { black, white, red }; //auto white = false; //erro! redefinition of 'white' as different kind of symbol
enum class Color { black, white, red }; auto white = false; Color c = Color::white;
void _f() { enum UnScopedColor { _black, _white, _red }; UnScopedColor c_ = _red; // double 型へ型変換される // scoped enum の場合コンパイルエラー // scoped enum でコンパイルを通す場合には、 // std::static_cast<double> で明示的に型変換を行う if (c_ < 14.5) { // pass } }
// enum Hoge; はコンパイルエラー enum class Hoge;
enum class Status: std::uint32_t; enum _Status: std::uint32_t;
using UserInfo = std::tuple<std::string, std::string, std::size_t>; UserInfo uInfo; // フィールド1 が何であるかを使う側が知っている必要がある... auto val = std::get<1>(uInfo); // unscoped enum を使うと...! enum UserInfoFields { uiName, uiEmail, uiReputation }; auto val2 = std::get<uiName>(uInfo); // scoped enum を使用すると冗長になる enum class UserInfoFields2 { uiName, uiEmail, uiReputation }; auto val3 = std::get<static_cast<std::size_t>(UserInfoFields2::uiName)>(uInfo);
- scoped enum で便利に、std::tuple の要素にアクセスするための関数テンプレート
// 任意の列挙子をとり、コンパイル時定数としてその値を返す関数テンプレート template<typename E> constexpr typename std::underlying_type<E>::type toUType(E enumerator) noexcept { return static_cast<typename std::underlying_type<E>::type>(enumerator); } // C++14 template<typename E> constexpr std::underlying_type_t<E> toUType2(E enumerator) { return static_cast<std::underlying_type_t<E>>(enumerator); } // C++14 のさらに改良版 template<typename E> constexpr auto toUType3(E enumerator) { return static_cast<std::underlying_type_t<E>>(enumerator); } auto val4 = std::get<toUType(UserInfoFields::uiName)>(uInfo); auto val5 = std::get<toUType2(UserInfoFields::uiName)>(uInfo); auto val6 = std::get<toUType3(UserInfoFields::uiName)>(uInfo);