只有在特定的编译时表达式为真时,才会使用try {.... } catch(..)。

11

我们试图做的是这样的

try {
    std::uninitialized_copy(...);
} catch(...) {
    if(!boost::has_trivial_destructor<T>::value) {
       // some cleanup to do here...
    }
    throw;
}

我们想知道,如果if中的编译时常量为false,那么try/catch是否会有成本。

编译器是否可以在其“as-if”权利范围内删除try/catch并表现得好像没有try包围它的std::uninitialized_copy调用一样?

还是C++规范中有一些隐藏的东西需要编译器将其留在这里?例如,想象一个假设的surrounding_try_blocks()函数,它返回当前框架周围动态尝试块计数。


请您详细说明您的问题。比如,当您说“try/catch是否有成本…”时,您指的是什么成本?仅仅添加try/catch块会带来一些性能成本。 - ravi
如果编译器优化掉了if语句,那么你的程序将与没有try/catch块时的功能完全相同,不是吗?因此,我不明白编译器还有什么开销需要优化。 - Red Alert
1
@Tommy 对的。例如,这段代码总是捕获任何异常(至少作为中间步骤)。如果删除 try,可能会出现一些异常永远不会被捕获的情况。我不太确定我是否理解了所有后果。如果调用不仅仅是 uninitialized_copy,而是将生命提升到更复杂的对象,例如,我非常确定我们可以有不同的可观察行为,因为标准允许编译器在异常最终没有被任何地方捕获时省略堆栈展开。 - Johannes Schaub - litb
1
我不明白为什么“if”部分是相关的。我的经验是编译器根本不会优化异常路径(甚至不会将try{throw 42;}catch(...){}优化为nop)。但只要没有抛出异常,在所谓的零成本异常处理实现中,您不应该注意到任何成本。对于您的具体问题,假设此块位于函数末尾,我的主要关注点是异常的复制/移动,但我认为在这种情况下允许省略。 - Marc Glisse
@MarcGlisse 我肯定会注意到一些成本。即使只是空间方面的。 - Johannes Schaub - litb
显示剩余3条评论
3个回答

1
我不知道编译器会做什么,但我知道你可以自己强制进行优化。
template <class T>
typename boost::enable_if_t<boost::has_trivial_destructor<T>::value, void>
wrap_uninitialized_copy (...) {
    std::uninitialized_copy(...);
}

template <class T>
typename boost::enable_if_t<!boost::has_trivial_destructor<T>::value, void>
wrap_uninitialized_copy (...) {
    try {
        std::uninitialized_copy(...);
    } catch(...) {
        // some cleanup to do here...
        throw;
    }
}

0

以下是我从各种来源整理出来的使用异常的成本总结。由于您在第二点提出的问题对我来说不太清楚,所以我认为最好向您介绍所有内容。希望您能选择一些重要的内容。

为了在运行时处理异常,程序必须进行相当数量的簿记工作。在执行过程中的每个点上,它们必须能够识别需要销毁的对象(如果抛出异常);它们必须记录每个try块的进入和退出;对于每个try块,它们必须跟踪相关的catch子句和这些子句可以处理的异常类型。

即使您从未使用任何异常处理功能,也会有一些费用。您需要支付用于跟踪完全构造的对象的数据结构所使用的空间,并且需要支付保持这些数据结构最新所需的时间。这些成本通常相当低。

尽管如此,没有支持异常的编译的程序通常比支持异常的程序编译得更快、更小。


4
但这并没有回答问题,“编译器是否可以在其“as-if”权利范围内删除try catch,并表现为std::uninitialized_copy调用出现在其周围没有try的情况下?”换句话说,您所描述的所有工作是否都可以被消除,因为编译器可以(原则上)看到它不需要? - Mike Seymour

0
从15.1/8我们发现

没有操作数的throw表达式会重新抛出当前处理的异常(15.3)。异常会使用现有的临时对象重新激活;不会创建新的临时异常对象...

对我来说,这相当明确地暗示着假设评估boost::has_trivial_destructor<T>::value可以被证明没有副作用,编译器应该能够轻松确定整个catch块是不可执行的,并且整个结构可以被优化掉。然而,我并不特别了解是否有任何编译器会/不会这样做。
我唯一(稍微)怀疑的是语言是否认为清除和重置std::uncaught_exception()在"as-if"条款之内。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接