未定义异常变量时按引用捕获

Chr*_*lin 2 c++ exception

捕获异常时,标准指导是按值抛出,通过引用捕获.据我了解,这有两个原因:

  1. 如果由于内存不足异常而抛出异常,我们将不会调用可能会终止程序的复制构造函数.
  2. 如果异常是继承层次结构的一部分,我们可能会对异常进行对象切片.

如果我们有一个我们没有在catch块中定义异常名称的场景,那么这些问题(实际上是1.,因为如果我们没有变量的名称,切片不会成为问题)仍然有效吗?

例如:

catch(my_exception)
{ ... }
Run Code Online (Sandbox Code Playgroud)

要么

catch(my_exception &)
{ ... }
Run Code Online (Sandbox Code Playgroud)

如果在这种情况下由值捕获的异常,是否仍有可能终止程序?我的感觉是技术上仍然可行.

注意:我问这个是因为我必须审查某人的代码,在这种情况下按值放置.如问题中所示,我不完全确定任何一种选择的技术影响,但我认为就整体而言,最好在这种情况下通过引用而不管(在任何情况下都没有通过引用捕获的缺点) .

Che*_*Alf 5

在未命名的异常对象的情况下,该标准不需要特殊优化.相反,它需要一个效果,好像临时是复制初始化.此复制可能导致内存被动态分配.

N3290§15.3/ 16:
如果异常声明指定了一个名称,它声明一个从异常对象复制初始化(8.5)的变量.如果exception-declaration表示对象类型但未指定名称,则从异常对象复制初始化(8.5)临时(12.2).在处理程序中初始化的任何自动对象的销毁之后,处理程序退出时变量或临时的生命周期结束.

上面的段落没有提到引用,因此可以合理地得出结论,无论异常对象是否被引用捕获,它都适用; 无论如何都要构建一个副本.

但是,这与下一段相矛盾:

N3290§15.3/ 17:
当处理程序声明一个非常量对象时,对该对象的任何更改都不会影响通过执行throw-expression初始化的临时对象.当处理程序声明对非常量对象的引用时,对引用对象的任何更改都是对执行throw-expression时初始化的临时对象的更改,并且如果该对象被重新抛出则会生效.

因此,声明的类型T&(带有Tconst)是C++ 11需要直接引用抛出对象而不是复制的单一情况.在C++ 03中也是这样,除了C++ 03有一些关于as-if优化的附加措辞.因此,对于正式,首选应该是

    catch( T& name )
Run Code Online (Sandbox Code Playgroud)

    catch( T& )
Run Code Online (Sandbox Code Playgroud)

但是,我总是遇到例外情况catch( T const& ).从实际的角度来看,可以假设编译器将优化它以直接引用抛出的对象,即使可以设计观察到的程序效果将是非标准的情况.例如<evil grin>

#include <stdio.h>
#include <stdexcept>

struct Error
    : std::runtime_error
{
public:
    static Error* pThrown;

    char const* pMessage_;
    Error()
        : std::runtime_error( "Base class message" )
        , pMessage_( "Original message." )
    {
        printf( "Default-construction of Error object.\n" );
        pThrown = this;
    }

    Error( Error const& other )
        : std::runtime_error( other )
        , pMessage_( other.pMessage_ )
    {
        printf( "Copy-construction of Error obejct.\n" );
    }

    char const* what() const throw() { return pMessage_; }
};

Error*  Error::pThrown  = 0;

int main()
{
    printf( "Testing non-const ref:\n" );
    try
    {
        throw Error();
    }
    catch( Error& x )
    {
        Error::pThrown->pMessage_ = "Modified message.";
        printf( "%s\n", x.what() );
    }

    printf( "\n" );
    printf( "Testing const ref:\n" );
    try
    {
        throw Error();
    }
    catch( Error const& x )
    {
        Error::pThrown->pMessage_ = "Modified message";
        printf( "%s\n", x.what() );
    }
}
Run Code Online (Sandbox Code Playgroud)

使用MinGW g ++ 4.4.1和Visual C++ 10.0,输出是......

Testing non-const ref:
Default-construction of Error object.
Modified message.

Testing const ref:
Default-construction of Error object.
Modified message

一个迂腐的形式主义者可能会说两个编译器都不符合规定,没有为Error const&案例创建副本.一个纯粹实际的从业者可能会说嘿,你还期待什么?而我,我说标准中的措辞在这里非常完美,如果有的话,人们应该期望澄清明确允许上面的输出,以便通过引用捕获const既安全又最有效.

总结一下.OP的问题:

  • 通过引用捕获不会调用可能会终止程序的复制构造函数.

  • 该标准仅保证此参考非const.

  • 实际上,如图所示,const即使计划结果受到影响,也可以保证参考.

干杯&hth.,