[dcl.attr.noreturn]提供了以下示例:
[[ noreturn ]] void f() {
throw "error";
// OK
}
但我不明白[[noreturn]]
的意义在哪里,因为该函数的返回类型已经是void
。
那么,noreturn
属性有什么作用?应该如何使用它?
[dcl.attr.noreturn]提供了以下示例:
[[ noreturn ]] void f() {
throw "error";
// OK
}
但我不明白[[noreturn]]
的意义在哪里,因为该函数的返回类型已经是void
。
那么,noreturn
属性有什么作用?应该如何使用它?
[[noreturn]]
属性用于表示某些函数不会返回到调用者。这并不意味着这些函数是 void
函数(这些函数会返回到调用者,只是它们不返回值),而是指函数在完成后控制流不会返回到调用函数(例如退出应用程序、无限循环或像您的示例中抛出异常的函数)。
编译器可以使用这个属性进行一些优化并生成更好的警告。例如,如果函数 f
具有 [[noreturn]]
属性,则当您编写 f(); g();
时,编译器可以警告您有关 g()
是死代码的情况。同样,编译器也将知道在调用 f()
后不需要警告您缺少 return
语句的情况。
noreturn
属性,如果控制流可能返回给调用者,则必须不使用它。只有在您的函数保证执行一些会在控制流返回给调用者之前终止程序的操作时才可以使用noreturn
属性--例如,因为您调用了exit(),abort(),assert(0)等。 - RavuAlHemionoreturn
函数外的catch
,或者从noreturn
函数内部抛出异常是不允许的? - Slipp D. Thompsontry
块中的任何代码仍然无法访问,并且如果不是void
类型,则任何赋值或使用返回值的操作都不会发生。 - Jon Hannanoreturn
并不是告诉编译器函数没有返回值,而是告诉编译器控制流程不会返回给调用者。这使得编译器能够进行各种优化-在调用周围不需要保存和恢复任何易失性状态,可以消除任何在调用之后执行的死代码等等。
[[maybe_noreturn]]
。例如,返回值的空函数会发出警告。如果你关闭或忽略了警告,那么你就会遇到其他问题。 - artless noise之前的回答正确地解释了什么是noreturn,但没有解释它为什么存在。我不认为“优化”注释是其主要目的:不返回值的函数很少,并且通常不需要进行优化。相反,我认为noreturn的主要存在理由是避免错误的正向警告。例如,考虑以下代码:
int f(bool b){
if (b) {
return 7;
} else {
abort();
}
}
如果abort()没有被标记为"noreturn",编译器可能会警告这段代码存在一条路径,f没有按预期返回一个整数。但是由于abort()被标记为不返回,所以编译器知道代码是正确的。
这意味着该函数不会完成。控制流永远不会触及调用f()
后的语句:
void g() {
f();
// unreachable:
std::cout << "No! That's impossible" << std::endl;
}
这些信息可以被编译器/优化器用于不同的目的。编译器可以添加一个警告,表示上面的代码是无法到达的,并且它可以以不同的方式修改 g()
的实际代码,例如支持continuations。
-Wno-return
进行编译,将会得到一条警告。可能并非您所期望的那种警告,但这已足以表明编译器知道[[noreturn]]
是什么并且可以利用它。(我有点惊讶-Wunreachable-code
没有起作用...) - David Rodríguez - dribeas[[noreturn]]
应该被中转应用到最外层的范围)。 - TemplateRex-O1
已经足够在没有[[noreturn]]
提示的情况下删除该不可达代码。 - TemplateRex[[noreturn]]
。如果这个翻译单元只有一个在其他地方定义的函数声明,编译器将无法删除该代码,因为它不知道该函数是否返回。这就是属性应该帮助编译器的地方。 - David Rodríguez - dribeas从类型理论的角度来说,void
在其他语言中被称为 unit
或者 top
。它在逻辑上等价于 True。任何值都可以合法地转换为 void
(每种类型都是 void
的子类型)。可以将其视为“宇宙”集合;世界上所有值没有共同的操作,所以对于类型为void
的值没有有效的操作。换句话说,告诉你某些东西属于宇宙集合并不会给你任何信息 - 你已经知道了。因此以下是正确的:
(void)5;
(void)foo(17); // whatever foo(17) does
但是下面的任务不是:
void raise();
void f(int y) {
int x = y!=0 ? 100/y : raise(); // raise() returns void, so what should x be?
cout << x << endl;
}
[[noreturn]]
有时也被称为empty
、Nothing
、Bottom
或Bot
,是逻辑上等价于False的。它没有任何值,该类型的表达式可以转换为(即为子类型)任何类型。这就是空集合。请注意,如果有人告诉您“表达式foo()的值属于空集合”,这非常有信息量-它告诉您此表达式永远不会完成其正常执行;它将会中止、抛出异常或挂起。这与void
完全相反。
因此,以下内容是没有意义的(伪C++,因为noreturn
不是一级C++类型):
void foo();
(noreturn)5; // obviously a lie; the expression 5 does "return"
(noreturn)foo(); // foo() returns void, and therefore returns
但是以下的任务是完全合法的,因为编译器理解 throw
不会返回:
void f(int y) {
int x = y!=0 ? 100/y : throw exception();
cout << x << endl;
}
在一个完美的世界中,您可以使用noreturn
作为上述函数raise()
的返回值:
在一个完美的世界中,你可以将noreturn
用作函数raise()
的返回值:
noreturn raise() { throw exception(); }
...
int x = y!=0 ? 100/y : raise();
遗憾的是C++不允许使用无返回值函数。可能是出于实际原因。相反,它提供了[[noreturn]]
属性,可以帮助指导编译器进行优化和警告。
void
,而且 void
永远无法被评估为 true
、false
或其他任何东西。 - Clearer(void)true;
是完全有效的。而 void(true)
在语法上是完全不同的东西。它试图通过使用 true
作为参数来调用一个构造函数来创建一个新的 void
类型对象;这会失败,其中一个原因是 void
不是一等类型。 - Elazar[[noreturn]]
的观点,用非常简单和简短的术语来表达。