Effective Modern C++ Item 9 and 10

20
Effective Modern C++ 勉強会 #2 Item 9, 10 内田 公太 (@uchan_nosサイボウズ株式会社 2015/02/25

Transcript of Effective Modern C++ Item 9 and 10

Page 1: Effective Modern C++ Item 9 and 10

Effective Modern C++勉強会 #2 Item 9, 10

内田公太(@uchan_nos)

サイボウズ株式会社

2015/02/25

Page 2: Effective Modern C++ Item 9 and 10

Item 9, 10

• Item 9: Prefer alias declarations to typedefs.

• Item 10: Prefer scoped enums to unscoped enums.

Page 3: Effective Modern C++ Item 9 and 10

Item 9: Prefer alias declarations to typedefs.typedefよりも別名型宣言を使おう

• typedef と using は同じような見た目

• using の技術的優位性はまだ見えない

typedefstd::unique_ptr<std::unordered_map<std::string, std::string>>

UPtrMapSS;

using UPtrMapSS =std::unique_ptr<std::unordered_map<std::string, std::string>>;

Page 4: Effective Modern C++ Item 9 and 10

Item 9: Prefer alias declarations to typedefs.typedefよりも別名型宣言を使おう

• ポインタが関係する例だと、using が解読しやすい

• が、明らかに using に優位性があるとは言えない

• テンプレートに関係してくると優位性がはっきりする

typedef void (*FP)(int, const std::string&);

using FP = void (*)(int, const std::string&);

Page 5: Effective Modern C++ Item 9 and 10

Item 9: Prefer alias declarations to typedefs.typedefよりも別名型宣言を使おう

• using はテンプレート化が可能(alias templates と呼ばれる機能)

• typedef はテンプレート化できないので、テンプレート化した struct 中に記述する→ その型を使うときにもっと深刻になる

template<typename T>using MyAllocList = std::list<T, MyAlloc<T>>;

template<typename T>struct MyAllocList {typedef std::list<T, MyAlloc<T>> type;

};

Page 6: Effective Modern C++ Item 9 and 10

Item 9: Prefer alias declarations to typedefs.typedefよりも別名型宣言を使おう

• typedef の MyAllocList<T>::type は型名なのかどうか、コンパイラには分からない“dependent type”

• using の MyAllocList<T> は型名だと分かる“non-dependent type”

template<typename T>class Widget {MyAllocList<T> list;

};

template<typename T>class Widget {typename MyAllocList<T>::type list;

};

typedefの場合

using の場合

Page 7: Effective Modern C++ Item 9 and 10

Item 9: Prefer alias declarations to typedefs.typedefよりも別名型宣言を使おう

• MyAllocList<Wine>::type は型名ではない!

• typename MyAllocList<T>::type と書く所以

class Wine { … };

template<>class MyAllocList<Wine> {enum class WineType{ White, Red, Rose };

WineType type;…

};

Page 8: Effective Modern C++ Item 9 and 10

Item 9: Prefer alias declarations to typedefs.typedefよりも別名型宣言を使おう

• <type_traits> で提供される型変換テンプレートはほとんどの場合テンプレートパラメタ T に対して使う→ typename を付け足す必要

• C++14 からは using 版の型変換が定義されている→ typename はもちろん不要、 ::type も不要

std::remove_const<T>::typestd::remove_reference<T>::typestd::add_lvalue_reference<T>::type

std::remove_const_t<T>std::remove_reference_t<T>std::add_lvalue_reference_t<T>

C++11 の場合

C++14 から

Page 9: Effective Modern C++ Item 9 and 10

Item 9: Prefer alias declarations to typedefs.typedefよりも別名型宣言を使おう

• C++14 が使えないとしても C++11 が使えさえすればOK

• 朝飯前の作業だし、さくっと定義しちゃお!

template <class T>using remote_const_t = typename remove_const<T>::type;

template <class T>using remote_reference_t =typename remove_reference<T>::type;

template <class T>using add_lvalue_reference_t =typename add_lvalue_reference<T>::type;

Page 10: Effective Modern C++ Item 9 and 10

Item 9: Prefer alias declarations to typedefs.typedefよりも別名型宣言を使おう

• typedef はテンプレート化をサポートしないが別名型宣言はサポートする

• 別名型テンプレートは ::type と typename が不要

• C++14 は type traits の全ての型変換に対して別名型テンプレートを用意している

覚えておくべきこと

Page 11: Effective Modern C++ Item 9 and 10

Item 10: Prefer scoped enums to unscoped enums.古いenumよりスコープ付きenumを使おう

• enum class は外部に識別子が漏れない• enum class を使う第1の理由

enum Color { black, white, red };

auto white = false; // error!

enum class Color { black, white, red };

auto white = false; // fine

Page 12: Effective Modern C++ Item 9 and 10

Item 10: Prefer scoped enums to unscoped enums.古いenumよりスコープ付きenumを使おう

• 名前空間が汚れないだけでも enum class を使う動機として十分

• 加えて enum class は強く型付けされる• enum class を使う第2の理由

enum class Color { black, white, red };

Color c = white; // error!

Color c = Color::white; // fine

auto c = Color::white; // fine (Item 5)

Page 13: Effective Modern C++ Item 9 and 10

Item 10: Prefer scoped enums to unscoped enums.古いenumよりスコープ付きenumを使おう

• 古い enum の列挙子は暗黙に整数型に変換できる

• すなわち、暗黙に浮動小数点数型にも変換できる

• class を付けるだけで、暗黙の型変換が無くなり、意味論の破たんを防止できる

enum Color { black, white, red };

std::vector<std::size_t> primeFactors(std::size_t x);

Color c = red;…if (c < 14.5) {auto factors = primeFactors(c);…

}

Page 14: Effective Modern C++ Item 9 and 10

Item 10: Prefer scoped enums to unscoped enums.古いenumよりスコープ付きenumを使おう

• どうしても Color 型から他の型に変換したいなら、型システムを歪めて無茶な希望に合わせるために、あなたがいつもやっていることをやりなさい――キャストを使うのです。

if (static_cast<double>(c) < 14.5) {auto factors =primeFactors(static_cast<std::size_t>(c));

…}

Page 15: Effective Modern C++ Item 9 and 10

Item 10: Prefer scoped enums to unscoped enums.古いenumよりスコープ付きenumを使おう

• enum class は前方宣言が出来る• enum class を使う第3の理由、に見えるかもしれない

• 実は C++11 では古い enum も前方宣言できる• enum Color: std::uint8_t;

enum Color; // error!

enum class Color; // fine

Page 16: Effective Modern C++ Item 9 and 10

Item 10: Prefer scoped enums to unscoped enums.古いenumよりスコープ付きenumを使おう

• すべての enum は “underlying type” を持つ

• 古い enum では、コンパイラが自動で決める• 上記の例では、0xFFFFFFFFが表せる最小の整数型

• または、最速な整数型になる、かもしれない

• enum class では、デフォルトが int と決まっている

enum Status { good = 0,failed = 1,incomplete = 100,corrupt = 200,indeterminate = 0xFFFFFFFF

};

Page 17: Effective Modern C++ Item 9 and 10

Item 10: Prefer scoped enums to unscoped enums.古いenumよりスコープ付きenumを使おう

• といっても、古い enum が役立つ場面はある

• std::tuple のフィールドを参照するとき、フィールド 1 が email に対応することを覚えてなんかいられない

• そこで古い enum が役立つ

using UserInfo = std::tuple<std::string, // namestd::string, // emailstd::size_t> // reputation

UserInfo uInfo;…auto val = std::get<1>(uInfo);

Page 18: Effective Modern C++ Item 9 and 10

Item 10: Prefer scoped enums to unscoped enums.古いenumよりスコープ付きenumを使おう

• これはひどい

enum UserInfoFields { uiName, uiEmail, uiReputation };

UserInfo uInfo;…auto val = std::get<uiEmail>(uInfo);

enum class UserInfoFields { uiName, uiEmail, uiReputation };

UserInfo uInfo;…auto val =std::get<static_cast<std::size_t>(UserInforFields::uiEmail)>(uInfo);

Page 19: Effective Modern C++ Item 9 and 10

Item 10: Prefer scoped enums to unscoped enums.古いenumよりスコープ付きenumを使おう

• 古い enum より依然としてタイプ量は多い

• 名前空間の汚染と意図しない変換は避けられる

• 2400bpsモデムの時代の最先端 enum 技術の落とし穴を避けるのに、多少のタイピング増加は妥当な対価である

template<typename E>constexpr autotoUType(E enumerator) noexcept

{return static_cast<std::underlying_type_t<E>>(enumerator);

}

auto val =std::get<toUType(UserInfoFields::uiEmail)>(uInfo);

Page 20: Effective Modern C++ Item 9 and 10

Item 10: Prefer scoped enums to unscoped enums.古いenumよりスコープ付きenumを使おう

• C++98 時代の enum は unscoped enum と呼ばれる

• scoped enum の列挙子はその enum の中だけで見え、他の型への変換は必ずキャストが必要

• scoped enum / unscoped enum ともに underlying type を指定できる• scoped enum のデフォルトは int

• unscoped enum はデフォルトを持たない

• scoped enum は前方宣言できる。 unscoped enum はunderlying type を指定したときだけ前方宣言できる。

覚えておくべきこと