【问题标题】:Enum class initialization with int使用 int 进行枚举类初始化
【发布时间】:2018-06-06 10:09:37
【问题描述】:

我在编程的时候发现了一些有趣的事情:

enum class Foo {
  FOO_THING,
  FOO_TOO
};

int main() {
  Foo foo{1};    // It is OK
  Foo foo2(1);   // It is an invalid
}

你能告诉我,为什么foo{1} 对编译器是可以的,为什么foo2(1) 是无效的?

编译器 GCC (g++ (Ubuntu 7.3.0-21ubuntu1~16.04) 7.3.0) 说:

$ g++ -Wall -std=c++17 foo.cpp

  error: cannot convert ‘int’ to ‘Foo’ in initialization
  Foo foo2(1);

我真的很想了解底层机制。 :)))

编辑:可能是一些编译器错误...

【问题讨论】:

  • 作为记录,VS2015 两者都出错。
  • 我添加一个关于编译器版本的注释
  • 显然,这是从 C++17 开始支持的。但我不知道哪个更改负责启用它。但是,Foo foo2(1) 失败,因为它是一个构造函数调用,并且枚举类没有采用 int 的构造函数。

标签: c++ enums initialization c++17


【解决方案1】:

特定于 C++17 的 documentation 具有以下括号初始化器

否则,如果 T 是一个有作用域的枚举类型,或者 具有固定基础类型的无范围,并且如果括号初始化列表具有 只有一个初始化器,如果从初始化器转换为 基础类型是非缩小的,如果初始化是 直接列表初始化,然后枚举初始化为 将初始化程序转换为其基础类型的结果。

所以foo 似乎符合有效的 C++17,但 foo2 没有大括号初始化是无效的。

【讨论】:

    【解决方案2】:

    要了解这两种语法不合法的原因,您必须考虑标准 c++11 引入了范围枚举以强制执行静态类型检查并具有范围标识符(即不再有名称污染)。

    Foo foo(1) 不起作用,因为从整数类型到作用域枚举的隐式转换是被禁止的,否则您将失去作用域枚举的好处,并避免在重载解析期间发生冲突。

    当使用Foo foo{1} 时,您使用的list initialization 也是由c++11 引入的,但是使用c++17 进行了升级,包括从int 值到枚举的隐式转换,如here 所报告的那样,如果满足一组要求:

    作用域枚举类型和非作用域枚举类型 基础类型是固定的,可以从一个整数初始化而没有 如果满足以下所有条件,则使用列表初始化进行强制转换:

    1. 初始化是直接列表初始化

    2. 初始化列表只有一个元素

    3. 枚举要么是作用域的,要么是非作用域的,底层类型是固定的

    4. 转换是非缩小的。

    这使得引入新的整数类型(例如 SafeInt)成为可能,这些类型享有与其底层整数相同的现有调用约定 类型,即使在 ABI 上通过以下方式惩罚传递/返回结构 价值。

    这种语法是安全的,不会干扰遗留代码(在 c++11 之前编写),因为当时不存在作用域枚举和列表初始化。此外,正如引用中所报告的,这可以使用新的整数类型(如 SafeInt 库中的整数类型),而无需在符合现代 c++ 语法的代码中强制对枚举类型进行静态转换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多