Liu*_*ick 2 c++ enums initialization language-lawyer
#include <cstdint>
enum struct option_t : uint8_t
{
INVALID = 0,
RESERVED = 1,
TEST_OPTION = 2
};
struct Trace_1
{
option_t option {option_t::INVALID};
uint32_t iter {0};
};
struct Trace_2
{
uint32_t iter {0};
option_t option {option_t::INVALID};
};
int main()
{
Trace_1 trace1{};
Trace_2 trace2{};
trace2 = {0};
trace1 = {0};
}
Run Code Online (Sandbox Code Playgroud)
对于上面的代码,列表初始化对一个临时对象进行了赋值。
Trace_1成员类型相同Trace_2但声明顺序不同;因此分配结果不同。
trace2 = {0}可以编译但是trace1 = {0}不能编译,报错:
<source>:32:12: error: no viable overloaded '='
trace1 = {0};
~~~~~~ ^ ~~~
<source>:15:8: note: candidate function (the implicit copy assignment operator) not viable: cannot convert initializer list argument to 'const Trace_1'
struct Trace_1
^
<source>:15:8: note: candidate function (the implicit move assignment operator) not viable: cannot convert initializer list argument to 'Trace_1'
struct Trace_1
Run Code Online (Sandbox Code Playgroud)
我认为初始化列表中的初始化程序0可以转换为两者中的成员类型,Trace_1并且Trace_2无需缩小转换,因此列表初始化分配应该有效。这里有什么想法吗?
在聚合初始化中,所有初始化程序(在您的情况下只是0)都用于按声明顺序初始化数据成员。
Trace_2,0初始化iter,其类型为uint32_t(OK)Trace_1,0初始化option_t为enum class(ERROR)您可能认为 using0必须对 有效Trace_1,因为option_t{0}是有效的 (C++17 起)。但是,成员的初始化是使用复制初始化进行的,就像:
option_t option = 0;
Run Code Online (Sandbox Code Playgroud)
没有从int到 的隐式转换option_t,因此这是不允许的。这与是否是缩小转换无关,而是与作用域枚举 ( ) 永远不允许的0 -> option_t语法有关。= 0enum class
附带说明一下,当尝试使用{0}初始化(而不是赋值)时,GCC 给我们带来了一个更好的错误trace1:
error: cannot convert 'int' to 'option_t' in initialization
24 | Trace_1 trace1{0};
| ^
| |
| int
Run Code Online (Sandbox Code Playgroud)
// works because we now use direct list init, not copy init
trace1 = {option_t{0}};
// works because we now use the default member initializers in Trace_1
trace1 = {};
// same as above; more explicit, slightly redundant style
trace1 = Trace_1{};
Run Code Online (Sandbox Code Playgroud)
注 1:您不需要在 C++ 中= {0}对 a 进行值初始化struct,只需在 C 中进行。除非您想明确将第一个成员初始化为零,否则不要这样做。
注 2:enum struct是允许的,但您可能更喜欢enum class,因为这是一种更流行的风格,并且会让更少的人感到惊讶。