noexcept
关键字可以适当地应用于许多函数签名,但我不确定何时应该考虑在实际中使用它。根据我到目前为止阅读的内容,最后一分钟添加的noexcept
似乎解决了移动构造函数抛出时出现的一些重要问题。但是,对于一些实际问题,我仍然无法提供令人满意的答案,这些问题导致我首先阅读更多关于noexcept
的内容。
>
有许多函数的例子,我知道它们永远不会抛出,但编译器无法自行确定这些函数。在所有这种情况下,我是否应该在函数声明中追加noexcept
?
必须考虑是否需要在每个函数声明后添加noexcept
会大大降低程序员的工作效率(坦率地说,这将是一种痛苦)。对于哪些情况,我应该更小心使用noexcept
;对于哪些情况,我可以使用隐含的noexcept(false)
?
在使用noexcept
后,我什么时候可以实际地看到性能改进?特别是,给出一个代码示例,在添加noexcept
之后,C++编译器能够生成更好的机器代码。
就我个人而言,我关心noexcept
,因为编译器安全地应用某些类型的优化提供了更多的自由度。现代编译器是否以这种方式利用noexcept
?如果不是,我是否可以期望他们中的一些人会在不久的将来这样做?
这实际上确实对编译器中的优化器产生了(潜在的)巨大的差异。编译器通过函数定义后的空throw()语句以及适当的扩展,实际上已经有了这个特性很多年了。我可以向您保证,现代编译器确实利用了这些知识来生成更好的代码。
几乎编译器中的每一个优化都使用一个叫做函数“流图”的东西来推理什么是合法的。流图由通常称为函数的“块”(具有单个入口和单个出口的代码区域)和块之间的边组成,这些边指示流可以跳到哪里。Noexcept更改流图。
你要求一个具体的例子。请考虑以下代码:
void foo(int x) {
try {
bar();
x = 5;
// Other stuff which doesn't modify x, but might throw
} catch(...) {
// Don't modify x
}
baz(x); // Or other statement using x
}
如果bar
被标记为noexcept
(执行无法在bar
的末尾和catch语句之间跳转),则该函数的流图会有所不同。当标记为noexcept
时,编译器确定在baz函数期间x的值是5-x=5块被称为“支配”baz(x)块,而没有从bar()
到catch语句的边缘。
然后它可以做一些叫做“恒定传播”的事情来生成更高效的代码。在这里,如果baz是内联的,那么使用x的语句也可能包含常量,然后可以将以前的运行时计算转换为编译时计算,等等。
总之,简短的答案:noexcept
让编译器生成一个更紧密的流图,流图用于推理各种常见的编译器优化。对于编译器来说,这种性质的用户注释是非常棒的。编译器会试图弄清楚这些东西,但它通常做不到(问题函数可能在编译器不可见的另一个对象文件中,或者可传递地使用一些不可见的函数),或者当它弄清楚时,可能会抛出一些您甚至没有意识到的琐碎异常,所以它不能隐式地将其标记为noexcept
(例如,分配内存可能会抛出bad_alloc)。
这几天我一直在重复:语义第一。
添加noexcept
、noexcept(true)
和noexcept(false)
首先是关于语义的。它只是附带地限制了一些可能的优化。
作为一个阅读代码的程序员,noexcept
的存在与const
的存在类似:它帮助我更好地了解可能发生或不发生的事情。因此,花点时间思考是否知道函数是否会抛出是值得的。对于一个提醒,任何类型的动态内存分配都可能抛出。
好了,现在来看看可能的优化。
最明显的优化实际上是在库中执行的。C++11提供了许多特性,允许了解函数是否为noexcept
,如果可能的话,标准库实现本身将使用这些特性来支持对其操作的用户html" target="_blank">定义对象进行noexcept
操作。例如移动语义。
编译器可能只会从异常处理数据中剃掉一点脂肪(也许),因为它必须考虑到您可能说谎的事实。如果标记为noexcept
的函数抛出,则调用std::terminate
。
选择这些语义有两个原因:
noexcept
(向后兼容性)noexcept
(向后兼容)
noexcept
的规范我认为现在给出一个“最佳实践”的答案还为时尚早,因为还没有足够的时间在实践中使用它。如果在抛出说明符后就问这个问题,那么答案将与现在大不相同。
必须考虑是否需要在每个函数声明后添加noexcept
会大大降低程序员的工作效率(坦率地说,这将是一种痛苦)。
好吧,那就在函数显然永远不会抛出的时候使用它。
在使用noexcept
后,我什么时候可以实际地看到性能改进?[...]就我个人而言,我关心noexcept
,因为编译器安全地应用某些类型的优化提供了更多的自由度。
似乎最大的优化收益来自用户优化,而不是编译器优化,因为可以检查noexcept
并在其上重载。大多数编译器遵循不抛出就不罚的异常处理方法,所以我怀疑它在代码的机器代码级别上会有多大的改变(或任何改变),尽管可能会通过删除处理代码来减少二进制大小。
在四大类(构造函数、赋值函数,而不是析构函数,因为它们已经是noexcept
)中使用noexcept
检查可能会带来最好的改进,因为noexcept
检查在模板代码(如std
容器)中是“常见的”。例如,std::vector
不会使用类的移动,除非它被标记为noexcept
(或者编译器可以用其他方式推导)。
问题内容: 有什么区别?什么时候应该使用容量为1的对抗? 问题答案: SynchronousQueue更像是一个传递,而LinkedBlockingQueue仅允许单个元素。区别在于对SynchronousQueue的put()调用直到有相应的take()调用 才返回 ,但LinkedBlockingQueue的大小为1,则put()调用(对空队列)将立即返回。 我不能说自己曾经直接使用过Sync
问题内容: 我对使用和翻译有疑问。我了解到,在模型中,我应该使用。但是还有其他地方我也应该使用吗?表单定义呢?它们之间是否存在性能差异? 编辑: 还有一件事。有时候,代替被使用。正如文档所述,仅在将字符串显示给用户之前,才将字符串标记为要翻译,并在可能的最新情况下进行翻译,但是我在这里有点困惑,这与功能相似吗?我仍然很难决定在模型和表格中应该使用哪个。 问题答案: ugettext() 与 uge
问题内容: 我知道他们两个都禁用了Nagle的算法。 我什么时候应该/不应该使用它们中的每一个? 问题答案: 首先,不是所有人都禁用Nagle的算法。 Nagle的算法用于减少有线中更多的小型网络数据包。该算法是:如果数据小于限制(通常是MSS),请等待直到收到先前发送的数据包的ACK,同时累积用户的数据。然后发送累积的数据。 这将对telnet等应用程序有所帮助。但是,在发送流数据时,等待A
问题内容: 在该类中,有两个字符串,和。 有什么不同?我什么时候应该使用另一个? 问题答案: 如果你的意思是和则: 用于在文件路径列表中分隔各个文件路径。考虑在上的环境变量。您使用a分隔文件路径,因此在上将是;。 是或用于拆分到特定文件的路径。例如在上,或
问题内容: 在集成我以前从未使用过的Django应用程序时,我发现了用于定义类中函数的两种不同方式。作者似乎非常有意地使用了它们。第一个是我自己经常使用的: 另一个是我不使用的,主要是因为我不知道何时使用它,以及什么用途: 在Python文档中,装饰器的解释如下: 类方法将类作为隐式第一个参数接收,就像实例方法接收实例一样。 所以我想指的是自己(而不是实例)。我不完全理解为什么会这样,因为我总是可
问题内容: 我仍然是React的菜鸟,在互联网上的许多示例中,我看到了渲染子元素时出现的这种变化,我感到困惑。通常我看到以下内容: 但是然后我看到一个这样的例子: 现在,我了解了api,但是文档并未确切说明我何时应该使用它。 那么,一个人做什么却另一个人不能做什么呢?有人可以用更好的例子向我解释吗? 问题答案: 编辑: 相反,请看Vennesa的答案,这是一个更好的解释。 原版的: 首先,该示例仅