是否有任何版本的C++(甚至是标准的)都是‘丢()’并不意味着“不能扔,永远”?

是否有任何版本的C++(甚至是标准的)都是‘丢()’并不意味着“不能扔,永远”?,c++,exception,language-lawyer,noexcept,exception-specification,C++,Exception,Language Lawyer,Noexcept,Exception Specification,问题是关于C++的历史:ISO标准、DR修订的标准,甚至是标准草案;全部计算为C++。 是否存在C++不包含该属性的C++: 使用空throw规范throw声明的函数无法引发异常 我想要一个反例,如果这个属性不成立 评论: 不用说,在函数中抛出异常,然后捕获而不重新抛出异常,这会使它成为抛出异常的函数。根据其规范,引发异常的函数将向其调用方引发异常。如果你在内部做一些事情,根据定义,它不是规范的一部分 [假设longjmp是被禁止的,因为我们有可破坏的对象。] 该问题的其他等效表述如下: 是否有

问题是关于C++的历史:ISO标准、DR修订的标准,甚至是标准草案;全部计算为C++。

是否存在C++不包含该属性的C++:

使用空throw规范throw声明的函数无法引发异常

我想要一个反例,如果这个属性不成立

评论:

不用说,在函数中抛出异常,然后捕获而不重新抛出异常,这会使它成为抛出异常的函数。根据其规范,引发异常的函数将向其调用方引发异常。如果你在内部做一些事情,根据定义,它不是规范的一部分

[假设longjmp是被禁止的,因为我们有可破坏的对象。]

该问题的其他等效表述如下:

是否有过这样的情况:声明的no-throw函数的调用方必须担心等价属性的可能性:

将控件返回给其调用方但不使用return语句的函数 查看并能够捕获该函数引发的异常 调用该函数会导致堆栈展开 如果调用该函数以外的所有其他操作都是非抛出的,则调用方不被非抛出 P>换句话说,C++中编译器是否无法根据调用函数的无抛出声明优化调用函数?

< P> C++ 17已经被弃用,主要是函数注释。它仍然允许抛出,并认为它等同于noexcepttrue。这意味着throw表示函数不应该因为异常而退出。违反noexcepttrue保证会导致未定义的行为

C++17之前的throw的语义是不同的。过去曾承诺,如果违反了throw子句,即抛出异常并导致函数退出,则会发生对::std::unexpected的调用

因此,这意味着在C++17之前,编译器仍然需要有某种机制来检测函数没有从异常中退出,即使它是用throw注释的。这是投掷的原因之一。。。不推荐使用括号之间带有stuff的函数注释,因为如果不删除throw注释的其他用途,将throw更改为等同于noexcepttrue是没有意义的

这段代码在编译器资源管理器中。如果您将请求的标准更改为C++17,您将看到该标准的代码消失

extern void a_different_function();

void test_function() throw ()
{
    a_different_function();
}
大多数情况下,C++17不赞成将throw作为函数注释。它仍然允许抛出,并认为它等同于noexcepttrue。这意味着throw表示函数不应该因为异常而退出。违反noexcepttrue保证会导致未定义的行为

C++17之前的throw的语义是不同的。过去曾承诺,如果违反了throw子句,即抛出异常并导致函数退出,则会发生对::std::unexpected的调用

因此,这意味着在C++17之前,编译器仍然需要有某种机制来检测函数没有从异常中退出,即使它是用throw注释的。这是投掷的原因之一。。。不推荐使用括号之间带有stuff的函数注释,因为如果不删除throw注释的其他用途,将throw更改为等同于noexcepttrue是没有意义的

这段代码在编译器资源管理器中。如果您将请求的标准更改为C++17,您将看到该标准的代码消失

extern void a_different_function();

void test_function() throw ()
{
    a_different_function();
}

换句话说,在看到调用函数的无抛出声明时,是否存在编译器无法优化调用函数的C++?< /p> 这个直接问题的答案是否定的,但仅此一点就具有极大的误导性

编译器对一个函数进行任何类型的优化的能力是极其有限的,因为该函数恰好调用了其他具有throw声明的函数。编译器能做的唯一真正的事情是消除处理调用函数中异常的任何代码的发出。但由于上述代码的性质,它实际上只适用于它调用的每个函数不抛出的情况。就调用抛出函数的函数的编译器优化而言,这几乎就是问题所在

今天人们经常谈论noexcept如何实现优化。这是真的;明智地使用noexcept可以使对此类函数进行操作的代码变得更加高效。但重要的是要记住,使用noexcept并不能实现编译器优化;它支持用户代码优化

让我们来看一个典型的例子,T的向量有noexcept移动支持。那件事不是真的 因为编译器会看到一系列副本,并自动将它们更改为move,这仅仅是因为move构造函数是noexcept。编译器甚至不可能做到这一点;不允许这样重新排列代码,因为这将是一个可检测的变化,取决于复制/移动构造函数所做的工作

这种情况会更快,因为vector的源代码实现检测到T是不可移动的。在检测到这种情况后,它将调用一个完全不同的代码路径来洗牌向量的元素。调用非抛出函数的代码变得更快,因为调用代码本身进行了优化,而不是因为编译器检测到任何东西

简单地说,调用函数的编译器优化从来不是真正的抛出点声明

换句话说,在看到调用函数的无抛出声明时,是否存在编译器无法优化调用函数的C++?< /p> 这个直接问题的答案是否定的,但仅此一点就具有极大的误导性

编译器对一个函数进行任何类型的优化的能力是极其有限的,因为该函数恰好调用了其他具有throw声明的函数。编译器能做的唯一真正的事情是消除处理调用函数中异常的任何代码的发出。但由于上述代码的性质,它实际上只适用于它调用的每个函数不抛出的情况。就调用抛出函数的函数的编译器优化而言,这几乎就是问题所在

今天人们经常谈论noexcept如何实现优化。这是真的;明智地使用noexcept可以使对此类函数进行操作的代码变得更加高效。但重要的是要记住,使用noexcept并不能实现编译器优化;它支持用户代码优化

让我们来看一个典型的例子,T的向量有noexcept移动支持。这种情况不会更快,因为编译器会看到一系列副本,并自动将它们更改为move,这仅仅是因为move构造函数不是except。编译器甚至不可能做到这一点;不允许这样重新排列代码,因为这将是一个可检测的变化,取决于复制/移动构造函数所做的工作

这种情况会更快,因为vector的源代码实现检测到T是不可移动的。在检测到这种情况后,它将调用一个完全不同的代码路径来洗牌向量的元素。调用非抛出函数的代码变得更快,因为调用代码本身进行了优化,而不是因为编译器检测到任何东西


简单地说,调用函数的编译器优化从来不是真正的抛出点声明。

各种与异常相关的函数注释的历史既漫长又肮脏。我相信你是对的。但很难确定。我很确定在C++17中使用throw规范是非法的。它们被贬低了。“如果不放弃,然后删除,那么就可以理解为什么,特别是对于其他PL设计人员。有没有C++在编译器无法根据调用函数的无抛出声明优化调用函数的情况下?”这和你的题目问的问题不一样。这需要知道优化调用函数的含义。关于非抛出的编译器优化主要围绕着优化抛出器,而不是抛出器。@Nicolas如果编译器没有零开销异常处理,那么知道非内联函数不能抛出可能是最不需要的事情,以确保块中没有任何东西可以抛出,因此这可能会产生很大的不同。希望这些实现异常(如返回码)的编译器现在很少见。各种与异常相关的函数注释的历史既漫长又肮脏。我相信你是对的。但很难确定。我很确定在C++17中使用throw规范是非法的。它们被贬低了。“如果不放弃,然后删除,那么就可以理解为什么,特别是对于其他PL设计人员。有没有C++在编译器无法根据调用函数的无抛出声明优化调用函数的情况下?”这和你的题目问的问题不一样。这需要知道优化调用函数的含义。关于非抛出的编译器优化主要围绕着优化抛出器,而不是抛出器。@Nicolas如果编译器没有零开销异常处理,那么知道非内联函数不能抛出可能是最不需要的事情,以确保块中没有任何东西可以抛出,因此这可能会产生很大的不同。希望这些实现异常(如返回代码)的编译器现在很少了。noexcept启用了优化
是的,运算符noexceptsome代码与显式或隐式声明函数的异常规范相结合。但是,旧的所谓动态异常规范抛出提供了与非动态异常规范抛出相同的用户优化好处?noexcept异常规范。@curiousguy:我不确定我是否理解你的观点。添加noexcept异常规范是为了修复抛出规范行为中的缺陷。我知道你知道这一点,因为答案正是这一点。在这里,您要问的是非抛出函数调用方的优化,这与为什么需要放弃抛出是不同的。要点很简单,作为与调用方的契约,旧的抛出规范起作用了。许多ppl声称它并没有真正起作用。@curiousguy:作为传递非类型对象的一种手段,void*也起作用。这并不意味着这是一个好主意,也不意味着没有更好的方法来达到同样的效果。因此,除了反驳这些虚构的许多ppl的说法之外,我个人从未听过有人说动态异常规范不建立契约。反对他们的主要论点是,他们订立的合同在很大程度上不是一个应该确立的东西,这有什么意义?即使不是因为这个原因,throw仍然需要被替换。被调用函数不能异常退出的契约不是有用的契约?这不是noexcept运算符测试的内容吗?noexcept启用了优化,而运算符noexceptsome代码是这样做的,它与显式或隐式声明函数的异常规范相结合。但是,旧的所谓动态异常规范抛出提供了与非动态异常规范抛出相同的用户优化好处?noexcept异常规范。@curiousguy:我不确定我是否理解你的观点。添加noexcept异常规范是为了修复抛出规范行为中的缺陷。我知道你知道这一点,因为答案正是这一点。在这里,您要问的是非抛出函数调用方的优化,这与为什么需要放弃抛出是不同的。要点很简单,作为与调用方的契约,旧的抛出规范起作用了。许多ppl声称它并没有真正起作用。@curiousguy:作为传递非类型对象的一种手段,void*也起作用。这并不意味着这是一个好主意,也不意味着没有更好的方法来达到同样的效果。因此,除了反驳这些虚构的许多ppl的说法之外,我个人从未听过有人说动态异常规范不建立契约。反对他们的主要论点是,他们订立的合同在很大程度上不是一个应该确立的东西,这有什么意义?即使不是因为这个原因,throw仍然需要被替换。被调用函数不能异常退出的契约不是有用的契约?这不是noexcept运算符测试的内容吗?一方面,在-std=c++11下,调用将执行test_函数:sub-rsp,8调用不同的函数add-rsp,8 ret,另一方面,w/-std=c++17将执行完全相同的代码。@curiousguy-但是为-std=c++17生成了大量额外的代码。实现异常返回的一种方法是在返回之前修改堆栈上的返回值。这里有一个小小的修改:-如您所见,它不再试图调用c++17中的意外处理程序。从技术上讲,我相信它可以假设应该抛出总是错误的,尽管它现在没有这样做。很明显,C++17对承诺不抛出异常的态度与C++11截然不同。@curiousguy-我有点错了。如果noexcept函数抛出,则行为不是未定义的。指定调用::std::terminate。因此,编译器可以进行的优化种类非常有限。我希望他们只是说这是不明确的行为@Omnifarious:请注意,终止所需的代码没有实现抛出行为所需的代码复杂,后者明确要求将堆栈展开到该点。一方面,在-std=c++11下,调用将执行test_函数:sub-rsp,8调用不同的函数add-rsp,8 ret,另一方面,w/-std=c++17将执行完全相同的代码。@curiousguy-但是为-std=c++17生成了大量额外的代码。实现异常返回的一种方法是在返回之前修改堆栈上的返回值。这里有一个小小的修改:-如您所见,它不再试图调用c++17中的意外处理程序。从技术上讲,我相信它可以假设应该抛出总是错误的,尽管它现在没有这样做。很明显,C++17对承诺不使用thr持完全不同的态度
这比C++11算是个例外。@curiousguy-我有点错了。如果noexcept函数抛出,则行为不是未定义的。指定调用::std::terminate。因此,编译器可以进行的优化种类非常有限。我希望他们只是说这是不明确的行为@Omnifarious:请注意,终止所需的代码没有实现抛出行为所需的代码复杂,后者明确要求将堆栈展开到该点。显然,它不一定是零,但它要小一些。