复制省略和例外

时间:2018-01-15 08:20:44

标签: c++ exception copy-constructor

在阅读cppreference的复制省略之后,我想玩例外和复制省略。 所以我在coliru

上用gcc 7.2编写了以下代码
#include <iostream>

class Exception
{
public:
    Exception() { std::cout << "constructed\n"; }
    Exception(Exception& other) {std::cout << "copy constructed\n";}
    Exception(const Exception& other) {std::cout << "copy constructed\n";}
    Exception(Exception&& other) {std::cout << "move constructed\n";}
};

void foo()
{
    throw Exception();
}

int main()
{
    try
    {
        foo();
    }
    catch(Exception e )
    {

    }
}

输出

  

构造
  复制构造

我们可以看到复制构造函数被调用,即使用-O2调用gcc也会发生这种情况。 在我看来,根据以下条款,此代码应该有资格复制elision:

  

处理异常时,如果catch子句的参数是   相同类型(忽略顶级cv资格)作为例外   抛出的对象,省略了副本和catch子句的主体   直接访问异常对象,就像通过引用捕获一样。

那为什么复制构造函数被调用?为什么复制省略在这种情况下不起作用?

1 个答案:

答案 0 :(得分:5)

cppreference对此不准确。事实上,除了常量表达式constexpr)或常量初始化(静态或线程本地)之外,实际上无法保证副本将被省略。在你的例子中情况并非如此。

请参阅当前C ++ 17草案中的[class.copy.elision]/1(强调我的):

  

当满足某些条件时,......在以下情况下,复制/移动操作的省略,称为复制省略, 允许

     

- 在 throw-expression 中,当操作数是非易失性自动对象的名称(函数或catch子句参数除外),其范围不会延伸到超出最里面的封闭try-block(如果有的话),通过将自动对象直接构造到异常对象中,可以省略从操作数到异常对象的复制/移动操作

     

复制elision是 必需 ,其中表达式在需要常量表达式常量初始化的上下文中进行评估>

这意味着有一天它可能被实现为编译器优化,但目前情况并非如此。

因此暂时catch const&是明智的(也是为了避免意外切片)。