限定作用域枚举类型与不限定作用域枚举类型的区别在[1]的条款10中有较为详细的描述,其中一个要点为:
限定作用域枚举型仅在枚举类型内可见,他们只能通过强制类型转换以转换到其他的类型,换言之不支持隐式的类型转换,举个例子:
enum class Color : int32_t { // 限定作用域枚举
black,
white,
red
};
int32_t color = Color::black; // compile error : cannot convert ‘Color’ to ‘int32_t’ {aka ‘int’} in initialization
enum Color : int32_t { // 非限定作用域枚举
black,
white,
red
};
int32_t color = Color::black; // compile ok
即限定作用的枚举类型不支持隐式类型转换,包括其自身的underlying_type;
std::is_enum
在C++11中提供了std::is_enum用于判断枚举类型:
#include <iostream>
#include <type_traits>
class A {};
enum E {};
enum class Ec : int {};
int main()
{
std::cout << std::boolalpha;
std::cout << std::is_enum<A>::value << '\n'; // false
std::cout << std::is_enum<E>::value << '\n'; // true
std::cout << std::is_enum<Ec>::value << '\n'; // true
std::cout << std::is_enum<int>::value << '\n'; // false
}
那么如何判断E和Ec?在C++23中会提供 std::is_scoped_enum
is_scoped_enum
在前文中说明限定作用域枚举类型不支持隐式转换为其underlying_type,因此可以利用这一点实现:
template <typename T, typename = std::void_t<>>
struct is_scoped_enum : std::false_type {};
template <typename T>
struct is_scoped_enum<T, std::void_t<std::enable_if_t<!std::is_convertible_v<T, std::underlying_type_t<T>> && std::is_enum_v<T>>>> : std::true_type {};
std::cout << is_scoped_enum<A>::value << '\n'; // false
std::cout << is_scoped_enum<E>::value << '\n'; // false
std::cout << is_scoped_enum<Ec>::value << '\n'; // true
std::cout << is_scoped_enum<int>::value << '\n'; // false
这里还可以修改为:
template <typename T, typename V = void>
struct Unscope : std::false_type {};
template <typename T>
struct Unscope<T, decltype(+T{})> : std::true_type {};
template <typename T>
using is_scoped_enum = std::integral_constant<bool, !Unscope<T>::value && std::is_enum<T>::value>;
这里引入一个知识点:
If the operand passed to a built-in arithmetic operator is integral or unscoped enumeration type, then before any other action (but after lvalue-to-rvalue conversion, if applicable), the operand undergoes integral promotion. [2]
一元+运算符会对整型(无限行作用域枚举类型)进行隐式integral promotion,即小整数类型(如 char)和无作用域枚举类型的纯右值可转换成较大整数类型(如 int)的纯右值。具体而言,算术运算符不接受小于 int 的类型作为它的实参,而在左值到右值转换后,如果适用就会自动实施整数提升。
备注: If an operand has array or function type, array-to-pointer and function-to-pointer conversions are applied. 例如lambda 表达式转成函数指针
因此限定作用域枚举类型不满足+T{}表达式,通过SFINE得到Unscope<T>::value的值为false;
若此时对一元+重载:
enum class Ec : int {
};
Ec operator+(const Ec &ec)
{
return Ec{};
}
让Ec支持一元加的隐式integral promotion,则判断错误:
std::cout << is_scoped_enum<Ec>::value << '\n'; // false
参考资料
【1】Effective Modern C++
【2】https://en.cppreference.com/w/cpp/language/operator_arithmetic
标签:std,作用域,enum,value,枚举,scoped,解析,type From: https://blog.51cto.com/u_13137973/8191903