Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么要动态检查C++0x的“noexcept”?_C++_Exception_C++11 - Fatal编程技术网

为什么要动态检查C++0x的“noexcept”?

为什么要动态检查C++0x的“noexcept”?,c++,exception,c++11,C++,Exception,C++11,我很好奇《新世纪》中“无例外”背后的理论基础。throwX遭到了反对,但noexcept似乎也做了同样的事情。在编译时没有检查noexcept有什么原因吗?如果静态检查这些函数,使其仅在try块中调用抛出函数,似乎会更好。如果我记得throw已被弃用,因为无法指定模板函数可以抛出的所有异常。即使对于非模板函数,您也需要throw子句,因为您添加了一些跟踪 另一方面,编译器可以优化不抛出异常的代码。有关详细讨论,请参见随零件和。要点似乎是: 过去二十年的大量经验表明,在实践中,只有两种形式的例外规

我很好奇《新世纪》中“无例外”背后的理论基础。throwX遭到了反对,但noexcept似乎也做了同样的事情。在编译时没有检查noexcept有什么原因吗?如果静态检查这些函数,使其仅在try块中调用抛出函数,似乎会更好。

如果我记得throw已被弃用,因为无法指定模板函数可以抛出的所有异常。即使对于非模板函数,您也需要throw子句,因为您添加了一些跟踪

另一方面,编译器可以优化不抛出异常的代码。有关详细讨论,请参见随零件和。要点似乎是:

过去二十年的大量经验表明,在实践中,只有两种形式的例外规范是有用的:

缺少公开异常规范,该规范指定可以引发任何类型异常的函数:

int func(); //might throw any exception
一个从不抛出的函数。这种功能可以通过抛出规范来表示:

int add(int, int) throw(); //should never throw any exception
请注意,noexcept检查由应用于空指针的失败动态_转换和typeid引发的异常,这只能在运行时完成。其他测试确实可以在编译时完成。

考虑一个函数

void fn() noexcept
{
   foo();
   bar();
}
你能静态检查它是否正确吗?您必须知道foo或bar是否会抛出异常。您可以强制所有函数调用位于try{}块内,类似这样

void fun() noexcept
{
    try
    {
         foo();
         bar();
    }
    catch(ExceptionType &)
    {
    }
}
但这是错误的。我们无法知道foo和bar只会抛出这种类型的异常。为了确保我们捕捉到我们需要使用的任何东西。。。。如果你发现了什么,你将如何处理你发现的错误?如果这里出现意外错误,唯一要做的就是中止程序。但这基本上就是默认情况下提供的运行时检查所要做的


简而言之,如果提供足够的细节来证明给定函数永远不会抛出不正确的异常,那么在编译器无法确定该函数是否会抛出错误类型的情况下,就会产生冗长的代码。静态证明的有用性可能不值得付出努力。

noexcept和throw的功能有些重叠,但它们实际上来自相反的方向

throw是关于正确性的,而不是其他:一种在抛出意外异常时指定行为的方法

noexcept的主要目的是优化:它允许/鼓励编译器围绕不会抛出异常的假设进行优化


但是,是的,实际上,它们是同一事物的两个不同名称。不过,它们背后的动机是不同的。

正如其他答案所述,诸如dynamic_cast之类的语句可能会抛出,但只能在运行时检查,因此编译器在编译时无法确定

这意味着在编译时,编译器可以让它们离开,也就是说,不要编译时检查、警告或直接拒绝它们,这将是无用的。这使得警告成为编译器唯一合理的操作

但是这仍然不是很有用——假设您有一个动态的_cast,无论您知道什么原因,它都不会失败并抛出异常,因为您的程序是这样编写的。编译器可能不知道这一点,并抛出一个警告,它变成了噪声,可能只是因为没有用而被程序员禁用,否定了警告的意义

类似的问题是,如果您有一个未使用noexcept ie指定的函数,则可以抛出您希望从许多函数调用的异常,有些是noexcept,有些不是。您知道该函数永远不会在noexcept函数调用的情况下抛出,但编译器也不会:更多无用的警告

因此,编译器在编译时没有有效的方法来强制执行这一点。这更多的是在静态分析领域,它往往更挑剔,并对此类事情发出警告。

基本上,这是一个链接器问题,标准委员会不愿意打破ABI。如果由我决定,我会这么做,它真正需要的是库的重新编译,我们已经有了线程支持的这种情况,而且是可以管理的

考虑一下结果会如何。假设要求是

每个析构函数都隐式为noexcepttrue 可以说,这应该是一项严格的要求。抛出析构函数总是一个错误。 每个外部C都隐式为noexcepttrue 同样的论点:C-land中的异常总是一个bug。 除非另有规定,否则其他每个函数都隐式为noexceptfalse noexcepttrue函数必须将其所有noexceptfalse调用包装在try{}catch…{} 通过类比,常量方法不能调用非常量方法。 此属性必须在重载解析函数p中显示为不同的类型 软膏兼容性等。 听起来很合理,对吧

为了实现这一点,链接器需要区分函数的noexcepttrue和noexceptfalse版本,就像可以重载成员函数的const和const版本一样

那么,这对名字Namming意味着什么呢?为了与现有的目标代码向后兼容,我们需要将所有现有名称解释为noexceptfalse,并对noexcepttrue版本进行额外的修改

这意味着我们不能链接现有的析构函数,除非修改头将它们标记为noexceptfalse

这将破坏向后兼容性, 这可以说是不可能的,见第1点。 我亲自和一位标准委员会成员谈过这件事,他说这是一个仓促的决定,主要是因为对集装箱移动操作的限制,否则可能会在抛掷后丢失物品,这违反了基本保证。请注意,这个人的设计理念是,不容忍错误的代码是好的。得出你自己的结论


就像我说的,我宁愿打破ABI也不愿打破语言。noexcept只是旧方法的一个微小改进。静态检查总是更好。

此外,第1107页有一个很好的脚注,非常有趣。哈哈!很好的发现。这是与重新定义不合格的程序一样好的,是一个在N3035中不好形成的WANABE C++程序。检查静态的问题是1不能真正地做得很好,而2即使这样做了,它仍然是一个主要的痛苦与工作-见证java等价物的问题。没有人真正想要C++中静态检查的异常说明符。问题更多的是例外说明符是否应该存在。实际上,有趣的脚注的问题在于,很容易将真实的脚注误认为有趣的脚注,例如342,除其他含义外,原子变量不应衰减。我不同意。如果我们有一个完整的foo和bar原型,那么我们就会知道它们是否有异常规范。即使foo和bar在动态加载的库中,这段代码也要编译的唯一方法是我们在编译时为它们提供适当的原型。@SoapBox:但唯一未弃用的规范是may throw和not throw。当foo和bar有可能抛出的规范时,fun抛出与否取决于它们可能抛出的内容。如果他们抛出ExceptionType对象,乐趣就可以了。如果他们抛出其他ExceptionType对象,乐趣就不好了。原型不足以确定这一点。此外,如果原型从nothrow更改为nothrow,该怎么办?但是,可以简单地禁止try块外的dynamic_cast或typeid…除非此时可能不适合捕获异常。可以重新抛出异常,但这会增加不必要的冗长和开销。抛出移动打破了以前正确代码(如排序算法和容器大小调整)的强大保证,因此我们需要std::move_if_noexcept以安全的方式重写旧的通用代码。当然,缺少静态检查使其行为类似于断言而非常量,但这是向后兼容性的代价:-正如spraff所说,模板代码(如容器)可以根据是否存在noexcept而表现不同,并等效地抛出,因此这不仅仅是编译器优化,但也会影响库的设计和算法的选择。实现这一点的关键是noexcept运算符,它允许代码查询表达式的使用方式,这是一个新事物,只需要回答是/否,它不关心可能引发什么类型的异常,只关心是否可能引发一个异常,如果可能的话,静态检查会更好。如果模块a中的noyhrow函数调用库B中的nothrow函数,则可以。直到库B变为抛出操作为止。每个外部C都隐式地不例外,我和其他人会对此表示反对。没有什么能阻止您从外部C函数抛出异常,并且在某些实现中,即使它被C代码调用,也可以让异常传播回C++代码,它知道如何处理它。GCC和Clang使用的异常处理模型明确地设计为支持这一点,Solaris也支持这一点。这是定义明确且有用的。如果没有C函数隐式地对异常规范进行非抛出静态检查,那么将无法工作。我不知道GCC/Clang是否支持跨语言异常,但该语言不支持它。让异常传播到不定义异常处理的代码中是不可能的。C没有定义异常处理。正确的方法是在到达C并将其包装到其他结构之前捕获异常。从C抛出异常是荒谬的。这可以检查吗
使用当前的C++11的编译器静态地编译,如果除了重载解决方案之外,没有满足这些约束,则生成警告?如果可以的话,那么如果警告变成了错误,它几乎和错误一样好。也许我是某种极端主义者,但我不认为正确性应该是编译器的选项。我不明白这个答案如何解释为什么noexcept没有被静态检查的原因。此外,Kalev先生的短语除了用作编译时异常规范之外,似乎与C++标准以及这个问题相矛盾。