220

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

[[ noreturn ]] void f() {
    throw "error";
    // OK
}

但我不明白这是什么意思[[noreturn]],因为函数的返回类型已经是void

那么,noreturn属性的意义何在?它应该如何使用?

4

5 回答 5

253

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

编译器可以使用它来进行一些优化并生成更好的警告。例如,如果f具有 noreturn 属性,编译器可能会g()在您编写f(); g();. 同样,编译器会知道在调用f().

于 2012-05-10T16:27:07.230 回答
71

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

于 2012-05-10T16:25:51.367 回答
32

这意味着该功能将无法完成。调用后控制流将永远不会命中语句f()

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

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

于 2012-05-10T16:25:22.410 回答
25

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

int f(bool b){
    if (b) {
        return 7;
    } else {
        abort();
    }
 }

如果 abort() 没有被标记为“noreturn”,编译器可能已经警告过这段代码的路径,其中 f 没有按预期返回整数。但是因为 abort() 被标记为 no return,所以它知道代码是正确的。

于 2017-11-22T22:23:54.207 回答
16

从理论上讲,类型void是其他语言中所说的unittop. 它的逻辑等价物是True。任何值都可以合法地转换为void(每种类型都是 的子类型void)。将其视为“宇宙”集合;世界上的所有值都没有共同的操作,因此对 type 的值没有有效的操作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或并且是False的逻辑等价物。它根本没有值,并且这种类型的表达式可以转换为任何类型(即它的子类型)。这是空集。请注意,如果有人告诉您“表达式 foo() 的值属于空集”,它会提供大量信息——它告诉您该表达式永远不会完成其正常执行;它会中止、抛出或挂起。它与 完全相反。NothingBottomBotvoid

所以以下没有意义(伪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() { throw exception(); }
...
int x = y!=0 ? 100/y : raise();

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

于 2015-01-08T17:47:02.863 回答