alf*_*lfC 5 c++ enums switch-statement c++11 enum-class
我通常使用clang我可以使用的所有合理警告来开发代码(-Wall -Wextra [-Wpedantic])。这种设置的switch好处之一是编译器会检查与所使用的枚举相关的stataments的一致性。例如在这段代码中:
enum class E{e1, e2};
int fun(E e){
switch(e){
case E::e1: return 11;
case E::e2: return 22; // if I forget this line, clang warns
}
}
Run Code Online (Sandbox Code Playgroud)
clang会抱怨(警告)如果: 我省略了e1或e2案例,并且没有默认案例。
<source>:4:12: warning: enumeration value 'e2' not handled in switch [-Wswitch]
switch(e){
Run Code Online (Sandbox Code Playgroud)
这种行为很好,因为
default我不会做任何好事的人为案例。int,它可能是一个没有默认构造函数的类型。(请注意,我使用的是 ,enum class所以我只假设有效的情况,因为无效的情况只能由调用者端的讨厌的强制转换生成。)
现在坏消息是: 不幸的是,当切换到其他编译器时,这很快就会崩溃。在 GCC 和 Intel (icc) 中,上面的代码警告(使用相同的标志)我不是从非 void 函数返回。
<source>: In function 'int fun(E)':
<source>:11:1: warning: control reaches end of non-void function [-Wreturn-type]
11 | }
| ^
Compiler returned: 0
Run Code Online (Sandbox Code Playgroud)
我为这项工作找到的唯一解决方案是有一个default案例并返回一个无意义的值。
int fun(E e){
switch(e){
case E::e1: return 11;
case E::e2: return 22;
default: return {}; // or int{} // needed by GCC and icc
}
}
Run Code Online (Sandbox Code Playgroud)
这很糟糕,因为我上面提到的原因(甚至没有涉及返回类型没有默认构造函数的情况)。但这也很糟糕,因为我可以再次忘记枚举案例之一,现在clang不会抱怨,因为有一个默认案例。
所以我最终做的是让这段丑陋的代码在这些编译器上运行,并在适当的时候发出警告。
enum E{e1, e2};
int fun(E e){
switch(e){
case E::e1: return 11;
case E::e2: return 22;
#ifndef __clang__
default: return {};
#endif
}
}
Run Code Online (Sandbox Code Playgroud)
或者
int fun(E e){
switch(e){
case E::e1: return 11;
case E::e2: return 22;
}
#ifndef __clang__
return {};
#endif
}
Run Code Online (Sandbox Code Playgroud)
有一个更好的方法吗?
这是示例:https : //godbolt.org/z/h5_HAs
在非默认可构造类的情况下,我真的完全没有好的选择:
A fun(E e){
switch(e){
case E::e1: return A{11};
case E::e2: return A{22};
}
#ifndef __clang__
return reinterpret_cast<A const&>(e); // :P, because return A{} could be invalid
#endif
}
Run Code Online (Sandbox Code Playgroud)
重要的是要注意,鉴于您对 的初始定义fun,执行以下操作在C++ 中是完全合法的:
fun(static_cast<E>(2));
Run Code Online (Sandbox Code Playgroud)
任何枚举类型都可以采用其表示的位数内的任何值。具有显式基础类型(enum class 始终具有基础类型;int默认情况下)的类型的表示是该基础类型的整体。因此,enum class默认情况下an可以采用 any 的值int。
这不是C++ 中的未定义行为。
因此,GCC 完全有权利假设fun可以获取其基础类型范围内的任何值,而不仅仅是其枚举数之一。
标准 C++ 对此并没有真正的答案。在理想的世界中,C++ 将有一个契约系统,您可以在其中预先声明fun要求参数e是枚举器之一。有了这些知识,GCC 就会知道交换机将采用所有控制路径。当然,即使 C++20 有契约(正在为 C++23 重新设计),仍然没有办法测试枚举值是否仅具有等于其枚举器之一的值。
在一个不太理想的世界中,C++ 将有一种方法可以明确地告诉编译器一段代码是不可访问的,因此编译器可以忽略执行到达那里的可能性。不幸的是,该功能也没有使 C++20 成为可能。
因此,目前,您只能使用特定于编译器的替代方案。
所有这三个编译器都有__builtin_unreachable()扩展名。您可以使用它来抑制警告(即使返回值存在构造函数问题)并引发更好的代码生成:
enum class E{e1, e2};
int fun(E e){
switch(e){
case E::e1: return 11;
case E::e2: return 22;
}
__builtin_unreachable();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
525 次 |
| 最近记录: |