【问题标题】:Copy elision and exceptions复制省略和异常
【发布时间】:2018-01-15 08:20:43
【问题描述】:

在阅读了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 也会发生这种情况。 在我看来,这段代码应该有资格根据以下条款复制省略:

处理异常时,如果catch子句的参数是 与异常相同的类型(忽略顶级 cv 限定) 抛出对象,省略副本和 catch 子句的主体 直接访问异常对象,就像被引用捕获一样。

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

【问题讨论】:

    标签: c++ exception copy-constructor


    【解决方案1】:

    cppreference 对此不准确。事实上,除了在常量表达式 (constexpr) 或常量初始化(静态或线程本地)中,不能保证副本会被忽略。在您的示例中并非如此。

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

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

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

    复制省略是必需,其中在需要常量表达式的上下文和常量初始化中计算表达式>.

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

    因此,明智的做法是暂时将catchconst&amp; 联系起来(同时避免意外切片)。

    【讨论】:

      【解决方案2】:

      只是,您的编译器在捕获异常时没有为您执行的省略是非强制性的。仅允许但没有义务执行省略。

      请注意,您的编译器在抛出时为您执行的省略也是非强制性的。

      目前https://en.cppreference.com/w/cpp/language/copy_elision是这样描述的。

      当然,你可以避免复制构造函数的这种调用,通过引用捕获异常, 要么

      catch (const Exception & e)
      

      catch (Exception & e)
      

      (根据您的需要)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-02
        • 1970-01-01
        • 1970-01-01
        • 2014-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-01
        相关资源
        最近更新 更多