【问题标题】:Unexpected copy constructor意外的复制构造函数
【发布时间】:2020-07-04 23:17:50
【问题描述】:

在下面的示例中,我预计只有一个副本构造,因为我认为中间副本将由 copy elided 生成。唯一需要的(我认为?)副本将在 B 的构造函数中初始化成员变量 a

#include <iostream>

struct A
{
    A() = default;
    A(A const&) { std::cout << "copying \n"; }
};

struct B
{
    B(A _a) : a(_a) {}
    A a;    
};

struct C : B
{
    C(A _a) : B(_a) {}
};

int main()
{
    A a{};
    C c(a);
}

当我execute this code(使用-O3)时,我看到以下输出

copying 
copying 
copying 

为什么不删除这些中间副本?

【问题讨论】:

  • 在这种情况下不能应用省略,并且 as-if 规则也不能应用,因为你有输出。

标签: c++ c++11 copy-elision


【解决方案1】:

以下是允许复制省略的情况(class.copy/31):

  • 在具有类返回类型的函数的 return 语句中,当表达式是 具有相同 cv- 的非易失性自动对象(函数或 catch 子句参数除外) 非限定类型作为函数返回类型,复制/移动操作可以通过构造省略 自动对象直接转化为函数的返回值

  • 在 throw 表达式中,当操作数是非易失性自动对象的名称时(除了 一个函数或 catch 子句参数),其范围不超出最内层的末尾 封闭的try-block(如果有的话),从操作数到异常的复制/移动操作 对象(15.1)可以通过将自动对象直接构造到异常对象中来省略

  • 何时复制/移动尚未绑定到引用 (12.2) 的临时类对象 对于具有相同 cv 非限定类型的类对象,可以通过以下方式省略复制/移动操作 将临时对象直接构造到省略的复制/移动的目标中
  • 当异常处理程序的异常声明(第 15 条)声明相同类型的对象时 (除了 cv-qualification)作为异常对象(15.1),复制/移动操作可以省略

对于您的示例,这些都不正确(我们不在返回语句、抛出表达式或异常声明中。而且您的示例中根本没有临时变量。),因此每次您期望发生时都会发生复制.

请注意,在上述情况下,允许复制省略,但不是强制性的。因此,即使在这些情况下,编译器也可以发出副本(这对于 C++11 是正确的。在 C++17 中,在某些情况下,复制省略是强制性的。但是,您的示例案例都不允许省略在 C++17 中。)

【讨论】:

    【解决方案2】:

    扩展另一个答案下的讨论,这里的副本有副作用(打印到控制台),因此不符合副本优化的条件。

    但是,根据 cppreference 关于复制省略的文章,无论副作用如何,都允许复制省略。这是不允许的,因为你一直都有左值。您希望消除的副本都不是右值或纯右值。要使它们成为右值,您需要使用 std::move 进行强制转换或将它们构造为无名临时对象作为构造函数调用的一部分。

    同样,cppreference 文章对此进行了更好的解释。

    【讨论】:

    • 重点不在于它是右值还是左值。即使 OP 使用std::move,也不允许省略移动构造。只有在非常特殊的情况下允许省略,在@geza 的答案中列出,与return 语句相关的情况甚至适用于左值。无名临时工只会帮助(也许)省略一个的副本/移动。
    • 是的,你是对的。直到我再次阅读列表时我才意识到。更仔细。
    【解决方案3】:

    您将 a 对象按值传递给 C 构造函数。然后按值传递给 B 构造函数。

    【讨论】:

    • 在此示例中有意按值传递参数。我的理解是arguments passed by value could/would also be elided
    • @CoryKramer 显然不是。
    • 我可以看到 :) 我希望能解释原因。班级有什么特殊要求会发生这种情况吗?编译器设置?在标准中提及何时可以/应该发生这种情况?
    • @CoryKramer 尝试在A 中添加任何数据成员。也许编译器不太关心数据为 0 的结构
    猜你喜欢
    • 2021-11-08
    • 1970-01-01
    • 2013-05-27
    • 2013-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-06
    相关资源
    最近更新 更多