>100 Views
February 25, 15
スライド概要
第二回 Effective Modern C++ 勉強会の資料です
Item 9 and 10
サイボウズ・ラボ株式会社で教育向けのOSやCPU、コンパイラなどの研究開発をしています。
Effective Modern C++ 勉強会 #2 Item 9, 10 内田 公太 (@uchan_nos) サイボウズ株式会社 2015/02/25
Item 9, 10 • Item 9: Prefer alias declarations to typedefs. • Item 10: Prefer scoped enums to unscoped enums.
Item 9: Prefer alias declarations to typedefs. typedef よりも 別名型宣言 を使おう typedef std::unique_ptr< std::unordered_map<std::string, std::string>> UPtrMapSS; using UPtrMapSS = std::unique_ptr< std::unordered_map<std::string, std::string>>; • typedef と using は同じような見た目 • using の技術的優位性はまだ見えない
Item 9: Prefer alias declarations to typedefs. typedef よりも 別名型宣言 を使おう typedef void (*FP)(int, const std::string&); using FP = void (*)(int, const std::string&); • ポインタが関係する例だと、using が解読しやすい • が、明らかに using に優位性があるとは言えない • テンプレートに関係してくると優位性がはっきりする
Item 9: Prefer alias declarations to typedefs.
typedef よりも 別名型宣言 を使おう
template<typename T>
struct MyAllocList {
typedef std::list<T, MyAlloc<T>> type;
};
template<typename T>
using MyAllocList = std::list<T, MyAlloc<T>>;
• using はテンプレート化が可能
(alias templates と呼ばれる機能)
• typedef はテンプレート化できないので、
テンプレート化した struct 中に記述する
→ その型を使うときにもっと深刻になる
Item 9: Prefer alias declarations to typedefs.
typedef よりも 別名型宣言 を使おう
template<typename T>
class Widget {
typename MyAllocList<T>::type list;
};
typedef の場合
template<typename T>
class Widget {
MyAllocList<T> list;
};
using の場合
• typedef の MyAllocList<T>::type は
型名なのかどうか、コンパイラには分からない
“dependent type”
• using の MyAllocList<T> は型名だと分かる
“non-dependent type”
Item 9: Prefer alias declarations to typedefs.
typedef よりも 別名型宣言 を使おう
class Wine { … };
template<>
class MyAllocList<Wine> {
enum class WineType
{ White, Red, Rose };
WineType type;
…
};
• MyAllocList<Wine>::type は型名ではない!
• typename MyAllocList<T>::type と書く所以
Item 9: Prefer alias declarations to typedefs. typedef よりも 別名型宣言 を使おう std::remove_const<T>::type std::remove_reference<T>::type std::add_lvalue_reference<T>::type C++11 の場合 std::remove_const_t<T> std::remove_reference_t<T> std::add_lvalue_reference_t<T> C++14 から • <type_traits> で提供される型変換テンプレートは ほとんどの場合テンプレートパラメタ T に対して使う → typename を付け足す必要 • C++14 からは using 版の型変換が定義されている → typename はもちろん不要、 ::type も不要
Item 9: Prefer alias declarations to typedefs. typedef よりも 別名型宣言 を使おう 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; • C++14 が使えないとしても C++11 が使えさえすればOK • 朝飯前の作業だし、さくっと定義しちゃお!
Item 9: Prefer alias declarations to typedefs. typedef よりも 別名型宣言 を使おう 覚えておくべきこと • typedef はテンプレート化をサポートしないが 別名型宣言はサポートする • 別名型テンプレートは ::type と typename が不要 • C++14 は type traits の全ての型変換に対して 別名型テンプレートを用意している
Item 10: Prefer scoped enums to unscoped enums.
古いenumよりスコープ付きenumを使おう
enum Color { black, white, red };
auto white = false; // error!
enum class Color { black, white, red };
auto white = false; // fine
• enum class は外部に識別子が漏れない
• enum class を使う第1の理由
Item 10: Prefer scoped enums to unscoped enums.
古いenumよりスコープ付きenumを使おう
enum class Color { black, white, red };
Color c = white;
// error!
Color c = Color::white; // fine
auto c = Color::white;
// fine (Item 5)
• 名前空間が汚れないだけでも enum class を使う動機と
して十分
• 加えて enum class は強く型付けされる
• enum class を使う第2の理由
Item 10: Prefer scoped enums to unscoped enums.
古いenumよりスコープ付きenumを使おう
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);
…
}
• 古い enum の列挙子は暗黙に整数型に変換できる
• すなわち、暗黙に浮動小数点数型にも変換できる
• class を付けるだけで、暗黙の型変換が無くなり、意味論
の破たんを防止できる
Item 10: Prefer scoped enums to unscoped enums.
古いenumよりスコープ付きenumを使おう
if (static_cast<double>(c) < 14.5) {
auto factors =
primeFactors(static_cast<std::size_t>(c));
…
}
• どうしても Color 型から他の型に変換したいなら、型シス
テムを歪めて無茶な希望に合わせるために、あなたがいつ
もやっていることをやりなさい――キャストを使うのです。
Item 10: Prefer scoped enums to unscoped enums. 古いenumよりスコープ付きenumを使おう enum Color; // error! enum class Color; // fine • enum class は前方宣言が出来る • enum class を使う第3の理由、に見えるかもしれない • 実は C++11 では古い enum も前方宣言できる • enum Color: std::uint8_t;
Item 10: Prefer scoped enums to unscoped enums. 古いenumよりスコープ付きenumを使おう enum Status { good = 0, failed = 1, incomplete = 100, corrupt = 200, indeterminate = 0xFFFFFFFF }; • すべての enum は “underlying type” を持つ • 古い enum では、コンパイラが自動で決める • 上記の例では、0xFFFFFFFFが表せる最小の整数型 • または、最速な整数型になる、かもしれない • enum class では、デフォルトが int と決まっている
Item 10: Prefer scoped enums to unscoped enums. 古いenumよりスコープ付きenumを使おう using UserInfo = std::tuple<std::string, // name std::string, // email std::size_t> // reputation UserInfo uInfo; … auto val = std::get<1>(uInfo); • といっても、古い enum が役立つ場面はある • std::tuple のフィールドを参照するとき、フィールド 1 が email に対応することを覚えてなんかいられない • そこで古い enum が役立つ
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);
• これはひどい
Item 10: Prefer scoped enums to unscoped enums.
古いenumよりスコープ付きenumを使おう
template<typename E>
constexpr auto
toUType(E enumerator) noexcept
{
return static_cast<std::underlying_type_t<E>>(
enumerator);
}
auto val =
std::get<toUType(UserInfoFields::uiEmail)>(uInfo);
• 古い enum より依然としてタイプ量は多い
• 名前空間の汚染と意図しない変換は避けられる
• 2400bpsモデムの時代の最先端 enum 技術の落とし穴を避け
るのに、多少のタイピング増加は妥当な対価である
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 を指定したときだけ前方宣言できる。