namespace std {
template <class T>
struct underlying_type {
using type = …;
};
template <class T>
using underlying_type_t = typename underlying_type<T>::type; // C++14
}
概要
列挙型の基底型を取得する。
C++11以降の列挙型(enum
/enum class
/enum struct
で定義された型)は、列挙子の値を表現するための基底型を指定できる:
// 基底型にcharを指定。
// 列挙子の値を表現するためにcharが使用される。
enum class CharColor : char {
Red, Green, Blue
};
// 基底型を指定しない場合、
// enum classではintがデフォルトの基底型となる。
enum class IntColor {
Red, Green Blue
};
underlying_type
を使用することで、列挙型に設定された基底型を取得できる。
要件
- C++11 : 型
T
が列挙型であること。(完全型を要求するかどうかは未規定) - C++14 : 型
T
が完全な列挙型であること。 - C++20 : 型
T
が不完全な列挙型ではないこと。(満たさない場合不適格)
効果
underlying_type
は、列挙型T
の基底型を、メンバ型type
として定義する。
C++20からはT
が列挙型ではない場合、type
は定義されない。
これによりSFINAEの文脈で使うときにこれまで不適格となるために列挙型以外の型のときに実体化を防ぐ必要があったところをその必要がなくなった。
#include <type_traits>
/**
Tが列挙型ではないときも、std::underlying_type_t<T>が実体化してしまっているため不適格
template<class T>
std::enable_if_t<std::is_enum<T>::value, std::underlying_type_t<T>>
foo(T t) { return static_cast<underlying_type_wrap_t<T>>(t); }
*/
#if 1
// p0340r3が適用されていない処理系
template<typename T, bool is_enum>
struct underlying_type_wrap_impl {};
template<typename T>
struct underlying_type_wrap_impl<T, true> : std::underlying_type<T> {};//列挙型に対する特殊化なのでOK
template<typename T>
struct underlying_type_wrap : underlying_type_wrap_impl<T, std::is_enum<T>::value> {};
template<typename T>
using underlying_type_wrap_t = typename underlying_type_wrap<T>::type;
template<typename T>
underlying_type_wrap_t<T> foo(T t) { return static_cast<underlying_type_wrap_t<T>>(t); }
#else
// C++20またはp0340r3が適用された処理系
// => 上のようなラッパーはいらない
template<typename T>
std::underlying_type_t<T> foo(T t) { return static_cast<std::underlying_type_t<T>>(t); }
#endif
template<typename T, std::enable_if_t<std::is_integral<T>::value, std::nullptr_t> = nullptr>
T foo(T t) { return t; }
enum class bar {
hoge
};
int main(){
return foo(bar::hoge) + foo(0);
}
例
#include <type_traits>
enum E1 : char {};
enum class E2 : char {};
enum E3 {};
enum class E4 {};
static_assert(std::is_same<std::underlying_type<E1>::type, char>::value, "E1 based type is char");
static_assert(std::is_same<std::underlying_type<E2>::type, char>::value, "E2 based type is char");
static_assert(std::is_integral<std::underlying_type<E3>::type>::value == true, "E3 based type is integral type");
static_assert(std::is_integral<std::underlying_type<E4>::type>::value == true, "E4 based type is integral type");
int main() {}
出力
バージョン
言語
- C++11
処理系
underlying_type
- Clang: 3.0 ✅
- GCC: 4.3.6 ✅
- Visual C++: 2012 ✅, 2013 ✅, 2015 ✅
P0340R3: Making std::underlying_type
SFINAE-friendly
以下の処理系ではコンパイル時の言語バージョンスイッチに関わらずP0340R3の修正が適用されている
- Clang: 9.0 ✅
- GCC: 9.1 ✅
- Visual C++: 2019 Update 5 ✅
関連項目
参照
- N2947 Additional Type Traits for C++0x
- 最初は
enum_base
という名前で提案されていた
- 最初は
- N2984 Additional Type Traits for C++0x (Revision 1)
enum_base
からunderlying_type
という名前に変更された
- N3546 TransformationTraits Redux
- N3655 TransformationTraits Redux, v2
- LWG Issue 2396. underlying_type doesn't say what to do for an incomplete enumeration type
- D0340R2: Making std::underlying_type SFINAE-friendly
- P0340R3: Making std::underlying_type SFINAE-friendly