我编程时发现了一些有趣的事情:
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);
我真的想知道潜在的机制。 :)))
编辑:也许这是一些编译器错误......
答案 0 :(得分:4)
C ++ 17特定的documentation具有以下支持初始化程序
否则,如果T是一个作用域的枚举类型 没有固定的底层类型,如果braced-init-list有 只有一个初始化程序,如果从初始化程序转换为 底层类型是非缩小的,如果初始化是 direct-list-initialization,然后使用初始化枚举 将初始值设定项转换为其基础类型的结果。
所以foo
似乎符合有效的C ++ 17,但foo2
未被支持初始化是无效的。
答案 1 :(得分:3)
要理解为什么这两种语法不是合法的原因,你必须考虑使用标准c ++ 11引入作用域枚举以强制执行静态类型检查并具有作用域标识符(即不再有名称污染)。
Foo foo(1)
无法正常工作,因为禁止从整数类型到作用域枚举的隐式转换,否则会失去作用域枚举的好处,并避免在重载解析期间发生冲突。
使用Foo foo{1}
时,您使用的是{+ 3}},它也是使用c ++ 11引入的,但是使用c ++ 17进行了升级,其中包含从int值到枚举的隐式转换list initialization,如果满足一系列要求:
两种范围的枚举类型和未范围的枚举类型 固定的底层类型可以从没有a的整数初始化 如果满足以下所有条件,则使用列表初始化强制转换:
初始化是直接列表初始化
初始化列表只有一个元素
枚举是作用域还是未作用域,基础类型已修复
- 醇>
转换不是缩小范围。
这使得引入新的整数类型(例如SafeInt)成为可能,这些整数类型享有与其基础整数相同的现有调用约定 类型,甚至在ABI上惩罚通过/返回结构 值。
这种语法是安全的,不会干扰遗留代码(在c ++ 11之前编写),因为当时不存在作用域枚举和列表初始化。此外,正如引文中所报告的,这使得能够使用新的整数类型(如SafeInt库的那些),而无需在符合现代c ++语法的代码中强制执行枚举类型的静态强制转换。