gcc:切换后如何最好地处理有关(无法访问)功能结束的警告?
gcc: how best to handle warning about (unreachable) end of function after switch?
当我以 c++11 标准编译以下代码时,它在 clang 和 gcc 上运行良好,但 gcc(我测试的所有版本 4.8.2、4.9.2、5.1.0)给出一个警告:
#include <iostream>
enum class FOO { A, B, C };
const char * bar(FOO f) {
switch (f) {
case FOO::A:
return "A";
case FOO::B:
return "B";
case FOO::C:
return "C";
}
}
int main() {
unsigned int x;
std::cin >> x;
FOO f = static_cast<FOO>(x % 3);
std::cout << bar(f) << std::endl;
}
警告是-Wreturn-type
:
main.cpp: In function ‘const char* bar(FOO)’:
main.cpp:14:1: error: control reaches end of non-void function [-Werror=return-type]
}
^
cc1plus: all warnings being treated as errors
即使使用 -O2
或 -O3
优化,我仍然会收到警告——这是否意味着即使在高优化级别,gcc 也无法通过死代码消除 'end'功能?
值得注意的是,它没有给我有关未处理的开关案例的警告。
编辑:从 godbolt 的实验来看,似乎即使在高级别,它 也不会 死代码消除它。我不确定它是否 可以 或者 clang 是否可以。
在这样的函数中是否有一种在本地抑制此警告的好方法,或者抑制此警告以通常禁用警告的唯一方法?
编辑:我猜这个问题提出了一个自然语言律师问题,从答案来看:
Can a conforming compiler dead-code eliminate the "end" of the bar
function in my listing? (Or 101010's version with return nullptr;
appended?) Or does conforming to the standard require that it generate code to handle enum values that aren't part of the enum definition?
我认为它可以通过死代码消除这一点,但欢迎您证明我错了。
为了抑制警告,将您的代码更改为:
const char * bar(FOO f) {
switch (f) {
case FOO::A:
return "A";
case FOO::B:
return "B";
case FOO::C:
return "C";
}
return nullptr;
^^^^^^^^^^^^^^^
}
或者:
const char * bar(FOO f) {
switch (f) {
case FOO::A:
return "A";
case FOO::B:
return "B";
case FOO::C:
return "C";
default:
return nullptr;
^^^^^^^^^^^^^^^
}
}
如果 f
不属于 switch
中提供的情况之一,编译器会警告您不会返回任何内容。非 void 函数的末尾下降而不返回会引入未定义的行为。
您的 bar 函数的结尾并非无法到达。如果我将代码更改为:
#include <iostream>
enum class FOO { A, B, C };
const char * bar(FOO f) {
switch (f) {
case FOO::A:
return "A";
case FOO::B:
return "B";
case FOO::C:
return "C";
}
std::cout << "End Reached" << std::endl;
//return nullptr;
}
int main() {
unsigned int x = 10;
FOO f = static_cast<FOO>(x);
std::cout << bar(f) << std::endl;
}
输出:
End Reached
我会在函数末尾添加一个assert(false)
:
#include <cassert>
const char * bar(FOO f) {
switch (f) {
case FOO::A:
return "A";
case FOO::B:
return "B";
case FOO::C:
return "C";
}
assert(false);
}
(请注意,将它添加为 default
情况不会那么好,因为这会在例如添加新枚举值时消除所有未处理的 switch 情况警告。)
这也有利于调试,因为当 bar
由于某种原因收到无效参数时,它会让您立即知道。
does this mean that even at high optimization levels, gcc cannot dead-code eliminate the 'end' of the function?
是的,因为它不是死代码。
标准允许您使用 static_cast<FOO>(static_cast<int>(FOO::B) | static_cast<int>(FOO::C))
作为参数调用您的函数。您的交换机无法处理此问题。
你没有收到关于你的开关没有处理的警告的原因是警告会 way 太多误报。
肯定不止一个"best way"。这个答案通过消除 switch
来解决问题。
...并且这种特殊方式可能不是您所期望的方式。我冒着被否决的风险,但我仍然发布这个。底线是:有时 "paradigm shift" 解决方案在给定的应用程序中比任何解决方法和警告禁用效果更好。
switch
是众所周知的"code smell"。我并不是说它总是不好,但有时你可以做得比切换更好,值得考虑替代方案。
在我们的例子中,bar
函数执行 translation(在语言意义上)。它在字典中查找一些键和 returns 一个翻译的词。
您可以使用标准容器来明确您的代码。经典的例子是std::map
(在其他编程语言中叫做"dictionary"):
#include <map>
enum class FOO {
A, B, C
};
// Might be a vector or even static array if your enumeration is contiguous
std::map<FOO, std::string> dict = {
{ FOO::A, "A" }, // maps ints to strings
{ FOO::B, "B" },
{ FOO::C, "C" },
};
int main() {
auto it = dict.find(FOO::A);
if (it == dict.cend()) {
// Handle "no such key" case
}
auto a = it->second; // "A"
}
无论选择哪种解决方案,如果字典中没有某些键,就会出现问题。无论如何你应该处理这个案子。
当我以 c++11 标准编译以下代码时,它在 clang 和 gcc 上运行良好,但 gcc(我测试的所有版本 4.8.2、4.9.2、5.1.0)给出一个警告:
#include <iostream>
enum class FOO { A, B, C };
const char * bar(FOO f) {
switch (f) {
case FOO::A:
return "A";
case FOO::B:
return "B";
case FOO::C:
return "C";
}
}
int main() {
unsigned int x;
std::cin >> x;
FOO f = static_cast<FOO>(x % 3);
std::cout << bar(f) << std::endl;
}
警告是-Wreturn-type
:
main.cpp: In function ‘const char* bar(FOO)’:
main.cpp:14:1: error: control reaches end of non-void function [-Werror=return-type]
}
^
cc1plus: all warnings being treated as errors
即使使用 -O2
或 -O3
优化,我仍然会收到警告——这是否意味着即使在高优化级别,gcc 也无法通过死代码消除 'end'功能?
值得注意的是,它没有给我有关未处理的开关案例的警告。
编辑:从 godbolt 的实验来看,似乎即使在高级别,它 也不会 死代码消除它。我不确定它是否 可以 或者 clang 是否可以。
在这样的函数中是否有一种在本地抑制此警告的好方法,或者抑制此警告以通常禁用警告的唯一方法?
编辑:我猜这个问题提出了一个自然语言律师问题,从答案来看:
Can a conforming compiler dead-code eliminate the "end" of the
bar
function in my listing? (Or 101010's version withreturn nullptr;
appended?) Or does conforming to the standard require that it generate code to handle enum values that aren't part of the enum definition?
我认为它可以通过死代码消除这一点,但欢迎您证明我错了。
为了抑制警告,将您的代码更改为:
const char * bar(FOO f) {
switch (f) {
case FOO::A:
return "A";
case FOO::B:
return "B";
case FOO::C:
return "C";
}
return nullptr;
^^^^^^^^^^^^^^^
}
或者:
const char * bar(FOO f) {
switch (f) {
case FOO::A:
return "A";
case FOO::B:
return "B";
case FOO::C:
return "C";
default:
return nullptr;
^^^^^^^^^^^^^^^
}
}
如果 f
不属于 switch
中提供的情况之一,编译器会警告您不会返回任何内容。非 void 函数的末尾下降而不返回会引入未定义的行为。
您的 bar 函数的结尾并非无法到达。如果我将代码更改为:
#include <iostream>
enum class FOO { A, B, C };
const char * bar(FOO f) {
switch (f) {
case FOO::A:
return "A";
case FOO::B:
return "B";
case FOO::C:
return "C";
}
std::cout << "End Reached" << std::endl;
//return nullptr;
}
int main() {
unsigned int x = 10;
FOO f = static_cast<FOO>(x);
std::cout << bar(f) << std::endl;
}
输出:
End Reached
我会在函数末尾添加一个assert(false)
:
#include <cassert>
const char * bar(FOO f) {
switch (f) {
case FOO::A:
return "A";
case FOO::B:
return "B";
case FOO::C:
return "C";
}
assert(false);
}
(请注意,将它添加为 default
情况不会那么好,因为这会在例如添加新枚举值时消除所有未处理的 switch 情况警告。)
这也有利于调试,因为当 bar
由于某种原因收到无效参数时,它会让您立即知道。
does this mean that even at high optimization levels, gcc cannot dead-code eliminate the 'end' of the function?
是的,因为它不是死代码。
标准允许您使用 static_cast<FOO>(static_cast<int>(FOO::B) | static_cast<int>(FOO::C))
作为参数调用您的函数。您的交换机无法处理此问题。
你没有收到关于你的开关没有处理的警告的原因是警告会 way 太多误报。
肯定不止一个"best way"。这个答案通过消除 switch
来解决问题。
...并且这种特殊方式可能不是您所期望的方式。我冒着被否决的风险,但我仍然发布这个。底线是:有时 "paradigm shift" 解决方案在给定的应用程序中比任何解决方法和警告禁用效果更好。
switch
是众所周知的"code smell"。我并不是说它总是不好,但有时你可以做得比切换更好,值得考虑替代方案。
在我们的例子中,bar
函数执行 translation(在语言意义上)。它在字典中查找一些键和 returns 一个翻译的词。
您可以使用标准容器来明确您的代码。经典的例子是std::map
(在其他编程语言中叫做"dictionary"):
#include <map>
enum class FOO {
A, B, C
};
// Might be a vector or even static array if your enumeration is contiguous
std::map<FOO, std::string> dict = {
{ FOO::A, "A" }, // maps ints to strings
{ FOO::B, "B" },
{ FOO::C, "C" },
};
int main() {
auto it = dict.find(FOO::A);
if (it == dict.cend()) {
// Handle "no such key" case
}
auto a = it->second; // "A"
}
无论选择哪种解决方案,如果字典中没有某些键,就会出现问题。无论如何你应该处理这个案子。