c++ - C ++:无法从相同类型的常量初始化枚举值
问题描述
由于未知原因,我无法从值初始化枚举constexpr
值。这是我的代码:
enum class Enum: unsigned int; //Forward declaration
constexpr Enum constant = static_cast<Enum>(2);
enum class Enum: unsigned int {
A = 0,
B = 1,
C = B, //This works
D = constant, //This FAILS
E = static_cast<unsigned int>(constant), //This works
F = Enum::B //This works
};
我无法理解的是为什么我可以写C = B
,但不能写D = constant
(B
并且constant
具有相同的类型!)
我仍然可以E = static_cast<unsigned int>(constant)
,但它太冗长了(在我的实际代码中,每个枚举值都是由constexpr
函数调用初始化的,而且很难放在static_cast<unsigned int>
任何地方)。
解决方案
以下所有标准参考均指N4659:2017 年 3 月 Kona 后工作草案/C++17 DIS。
C ++:无法从相同类型的常量初始化枚举值
首先,枚举类型和它的底层类型是不一样的,前者的枚举数应该用底层类型的constexpr值或隐式可转换为底层类型的常量表达式来定义。
正如[dcl.enum]/10的非规范示例中所述,范围枚举和整数之间没有隐式转换,甚至没有明确指定的固定基础类型:
枚举数或非范围枚举类型的对象的值通过整数提升转换为整数。[ <em>示例:[...]
请注意,没有为作用域枚举提供此隐式枚举到 int 的转换:
enum class Col { red, yellow, green }; int x = Col::red; // error: no Col to int conversion Col y = Col::red; if (y) { } // error: no Col to bool conversion
— <em>结束示例]
并且,由于[conv.integral]/1和[conv.prom]/4的规范文本中不存在范围枚举([conv.prom]/3用于其基础类型不固定的无范围枚举类型) ) [强调我的]:
[conv.integral]/1
整数类型的纯右值可以转换为另一种整数类型的纯右值。无作用域枚举类型的纯右值可以转换为整数类型的纯右值。
[conv.prom]/4
基础类型固定 ([dcl.enum])的无作用域枚举类型的纯右值可以转换为其基础类型的纯右值。此外,如果可以将整型提升应用于其基础类型,则其基础类型固定的无范围枚举类型的纯右值也可以转换为提升的基础类型的纯右值。
因此,您的程序,尤其是枚举定义D = constant
,格式不正确。
实际上,如果我们修改您的示例,将Enum
其更改为非范围枚举,则该程序不再是格式错误的。
enum Enum: unsigned int; //Forward declaration
constexpr Enum constant = static_cast<Enum>(2);
enum Enum: unsigned int {
A = 0,
B = 1,
C = B, // Ok
D = constant, // Ok, implicit conversion
E = static_cast<unsigned int>(constant) // Ok
};
int main() { }
那为什么
C = B
在我的代码中有效?
由于有些棘手的条款[dcl.enum]/5,它指出
在枚举说明符的右大括号之后,每个枚举数都有其 enumeration 的类型。如果基础类型是固定的,则右大括号之前的每个枚举数的类型都是基础类型[...]。如果基础类型不是固定的,则右大括号之前的每个枚举数的类型确定如下:
- [...]
用外行的话来说,每个枚举器的类型基本上都会根据它是从枚举定义内部还是从枚举定义之外来看待而改变。
这意味着在枚举定义内部,我们可以在后面的枚举定义中使用先前定义的枚举数,因为它们都有类型(忽略[dcl.enum]/5.1和[dcl.enum]/5.3中的一些细节)对于底层类型不固定的枚举),无论枚举是否有作用域(即不需要隐式转换),而在枚举的定义之外,这些枚举数与枚举的类型相同本身。
#include <type_traits>
enum class Enum: unsigned int; //Forward declaration
constexpr Enum constant = static_cast<Enum>(0);
// Forward a constexpr value whilst asserting
// type identity between two type template parameters.
template<typename T, typename U, unsigned int VALUE>
struct assert_and_get_value {
static_assert(std::is_same_v<T, U>, "");
static constexpr unsigned int value = VALUE;
};
enum class Enum: unsigned int {
A = 1,
B,
// C and B here are both of type 'unsigned int'
C = B,
D = assert_and_get_value<decltype(C), unsigned int, 5>::value,
// Note that 'constant', however, in this scope, has type 'Enum'.
E = assert_and_get_value<decltype(constant), const Enum, 6>::value,
};
// At this point, however, the type of each enumerator
// is the type of the enum.
static_assert(std::is_same_v<decltype(Enum::A), Enum>, "");
static_assert(!std::is_same_v<decltype(Enum::A), unsigned int>, "");
int main() {}
推荐阅读
- c# - 如何从另一个表单访问 DataGridView?
- view - 是否可以在执行期间同时显示来自不同代理的所有状态图?
- scala - 无法通过火花流从本地计算机连接到 kafka 端点
- javascript - Material-ui TextField dom元素如何自定义
- apache-spark - PySpark - 将字符串转换为数组
- java - 使用 adjustResize 防止键盘在启动时自动弹出
- php - 已解决找不到驱动程序 Debian SQL Server PHP
- javascript - 加载 Babel 的最佳方式是什么?
- html - 中文单词在探险者中变得一团糟
- kubernetes - 错误 503 后端获取失败 Guru Meditation: XID: 45654 Varnish 缓存服务器