noreturn有什么意义?

BЈо*_*вић 178 c++ attributes c++11 noreturn

[dcl.attr.noreturn]提供以下示例:

[[ noreturn ]] void f() {
    throw "error";
    // OK
}
Run Code Online (Sandbox Code Playgroud)

但我不明白有什么意义[[noreturn]],因为函数的返回类型已经存在void.

那么,该noreturn属性的重点是什么?它应该如何使用?

sep*_*p2k 200

noreturn属性应该用于不返回调用者的函数.这并不意味着void函数(它们确实返回调用者 - 它们只是不返​​回值),而是在函数完成后控制流不会返回到调用函数的函数(例如退出应用程序的函数,永远循环或抛出例子中的异常).

编译器可以使用它来进行一些优化并生成更好的警告.例如,如果f具有noreturn属性,编译器可能会g()在您编写时警告您是否为死代码f(); g();.类似地,编译器将知道在调用之后不会警告您缺少return语句f().

  • 不,它不应该 - 如果控制流有可能返回到调用者,它必须没有`noreturn`属性.只有当你的函数保证在控制流可以返回调用者之前做一些终止程序的事情时才可以使用`noreturn` - 例如,因为你调用exit(),abort(),assert(0)等. (19认同)
  • @ SlippD.Thompson不,这是不可能的.抛出一个异常并没有返回,所以如果每个路径抛出那么它就是'noreturn`.处理该异常与返回的异常不同.调用后`try`中的任何代码仍然无法访问,如果不是'void`,则不会发生任何赋值或使用返回值. (7认同)
  • @ SlippD.Thompson如果对一个noreturn函数的调用被包装在一个try-block中,那么来自catch块的任何代码都将被计为再次可达. (6认同)
  • 怎么样的函数,如[`execve`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html),_should not_ return但_could_?它应该有_noreturn_属性吗? (2认同)
  • 这是否包括通过异常抛出返回(可以这么说),或者抛出的异常跳过 `noreturn` 函数之外的 `catch`,还是不允许从 `noreturn` 函数内抛出异常? (2认同)
  • @ sepp2k酷.所以回归并非不可能,只是异常.那很有用.干杯. (2认同)

Ste*_*non 60

noreturn不告诉编译器该函数不返回任何值.它告诉编译器控制流不会返回给调用者.这允许编译器进行各种优化 - 它不需要保存和恢复调用周围的任何易失性状态,它可以使用死代码来消除否则将跟随调用的任何代码等.


Dav*_*eas 26

这意味着该功能无法完成.调用后,控制流程永远不会出现在语句中f():

void g() {
   f();
   // unreachable:
   std::cout << "No! That's impossible" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

编译器/优化器可以以不同方式使用该信息.编译器可以添加上述代码无法访问的警告,并且可以g()以不同方式修改实际代码,例如支持延续.

  • @TemplateRex:使用`-Wno-return`编译,你会收到警告.可能不是你期望的那个,但可能足以告诉你编译器知道`[[noreturn]]`是什么,它可以利用它.(我很惊讶`-Wunreachable-code`没有开始......) (4认同)
  • gcc/clang [不要发出警告](http://coliru.stacked-crooked.com/view?id=5563e13b0ff475ba4131e93d7c461093-eae62b55f4d064645734b0b710d33419) (3认同)
  • @TemplateRex:抱歉`-Wmissing-noreturn`,警告暗示流分析确定`std :: cout`无法访问.我手头上没有足够新的gcc来查看生成的程序集,但是如果对`operator <<的调用被删除,我不会感到惊讶 (3认同)
  • @TemplateRex:所有代码都在同一个翻译单元中并且可见,因此编译器可以从代码中推断出`[[noreturn]]`.如果此转换单元仅具有在其他位置定义的函数的声明,则编译器将无法删除该代码,因为它不知道该函数未返回.这是属性应该帮助编译器的地方. (2认同)

Nad*_*'El 14

以前的答案正确地解释了noreturn是什么,但不是为什么它存在.我不认为"优化"注释是主要目的:不返回的函数很少,通常不需要优化.相反,我认为noreturn的主要存在理由是避免误报警告.例如,考虑以下代码:

int f(bool b){
    if (b) {
        return 7;
    } else {
        abort();
    }
 }
Run Code Online (Sandbox Code Playgroud)

如果abort()没有被标记为"noreturn",编译器可能会警告这个代码有一个路径,其中f不会按预期返回一个整数.但是因为abort()被标记为没有返回它知道代码是正确的.

  • 我示例中的 noreturn 函数不是 f(),而是 abort()。标记非空函数 noreturn 没有意义。有时返回值有时返回的函数(一个很好的例子是 execve())不能被标记为 noreturn。 (5认同)

Ela*_*zar 11

键入从理论上讲,void就是所谓的在其他语言unittop.它的逻辑等价是True.任何值都可以合法地转换为void(每种类型都是子类型void).把它想象成"宇宙"集; 世界上的所有值都没有共同的操作,因此对类型值没有有效的操作void.换句话说,告诉你属于宇宙集的东西不会给你任何信息 - 你已经知道了.所以以下是合理的:

(void)5;
(void)foo(17); // whatever foo(17) does
Run Code Online (Sandbox Code Playgroud)

但下面的任务不是:

void raise();
void f(int y) {
    int x = y!=0 ? 100/y : raise(); // raise() returns void, so what should x be?
    cout << x << endl;
}
Run Code Online (Sandbox Code Playgroud)

[[noreturn]],另一方面,有时也被称为empty,Nothing,BottomBot和是的逻辑等效.它根本没有值,并且这种类型的表达式可以转换为任何类型的(即子类型).这是空集.请注意,如果有人告诉你"表达式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
Run Code Online (Sandbox Code Playgroud)

但是下面的赋值是完全合法的,因为throw编译器可以理解为不返回:

void f(int y) {
    int x = y!=0 ? 100/y : throw exception();
    cout << x << endl;
}
Run Code Online (Sandbox Code Playgroud)

在完美的世界中,您可以使用noreturn上述函数的返回值raise():

noreturn raise() { throw exception(); }
...
int x = y!=0 ? 100/y : raise();
Run Code Online (Sandbox Code Playgroud)

可悲的是,C++不允许它,可能是出于实际原因.相反,它使您能够使用[[ noreturn ]]有助于指导编译器优化和警告的属性.

  • 在讨论该语言的类型系统时,不符合特定语言类型系统的抽象类型理论无关紧要.问题是:-),是关于C++,而不是类型理论. (8认同)
  • `(void)true;`完全有效,正如答案所暗示的那样.`void(true)`在语法上完全不同.尝试通过调用带有`true`作为参数的构造函数来创建一个类型为`void`的新对象; 除了其他原因,这失败了,因为`void`不是头​​等舱. (8认同)
  • 什么都没有被强制转换为`void`和`void`永远不会评估为'true`或`false`或其他任何东西. (5认同)
  • 当我说'true`时,我的意思并不是"bool`类型的值'true`,而是逻辑意义,请参阅[Curry-Howard对应](https://en.wikipedia.org/wiki/咖喱%E2%80%93Howard_correspondence) (4认同)
  • 一切都可能被强制转换为“void”。并且 `void` 永远不会评估为 `true`,因为 `bool` 是 `void` 的子类型,而不是相反。同样,这里所说的一切都是类型理论的。没有什么以 C++ 为中心的。`void` 不是 C++ 类型系统中的一等公民。 (2认同)
  • 针对这个问题,抽象类型理论给出了完美的答案。在这种情况下,C++ 并没有残酷地违反它(因为它几乎是一致的)。 (2认同)
  • 就是这样.我习惯于使用类型(值)转换,而不是c样式转换. (2认同)
  • 这个答案与OP的问题无关. (2认同)
  • @JoshSanford它解释了类型理论术语中"void"和"noreturn"之间的区别,补充了上述(实际上更有用)实际答案. (2认同)
  • @Elazar:`void(foo())` 很好,但如果它是一个完整的语句,那么它就是一个函数声明。 (2认同)