当前位置: 首页 > 知识库问答 >
问题:

什么是复制删除和返回值优化?

齐英韶
2023-03-14

什么是拷贝删减?什么是(命名的)返回值优化?它们暗示了什么?

在什么情况下它们会发生?限制是什么?

  • 如果您被引用到此问题,您可能正在查找介绍。
  • 有关技术概述,请参阅标准参考。
  • 请参阅此处的常见情况。

共有2个答案

诸葛苏燕
2023-03-14

要获得一个不太技术性的视图&介绍-跳到这个答案。

对于复制删除发生的常见情况-跳到此答案。

复制删除在标准中定义为:

作为

31)当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数有副作用。在这种情况下,该实现将省略的复制/移动操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的破坏发生在两个对象在没有优化的情况下本来会被破坏的时间中的较晚的时间。123复制/移动操作的这种省略,称为复制省略,在以下情况下是允许的(可以将其组合以消除多个副本):

-在具有类返回类型的函数中的返回语句中,当表达式是具有与函数返回类型相同的cvqualified类型的非易失性自动对象(不是函数或catch-clause参数)的名称时,可以通过将自动对象直接构造到函数的返回值中来省略复制/移动操作

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

-当尚未绑定到引用(12.2)的临时类对象将被复制/移动到具有相同CV-qualified类型的类对象时,可以通过将临时对象直接构造到被省略的复制/移动的目标中来省略复制/移动操作

-当一个异常处理程序的异常声明(第15条)声明一个相同类型的对象(cv-qualifience除外)作为异常对象(15.1)时,如果程序的意义除了对异常声明所声明的对象执行构造函数和析构函数之外将保持不变,则可以通过将异常声明视为异常对象的别名来省略复制/移动操作。

123)因为只销毁了一个对象而不是两个对象,并且没有执行一个复制/移动构造函数,所以每构造一个对象,仍有一个对象被销毁。

给出的例子是:

class Thing {
public:
  Thing();
  ~Thing();
  Thing(const Thing&);
};
Thing f() {
  Thing t;
  return t;
}
Thing t2 = f();

并解释道:

在这里,可以组合省略的条件,以消除对类thing的复制构造函数的两个调用:将本地自动对象t复制到函数f()的返回值的临时对象中,以及将该临时对象复制到对象t2中。有效地,局部对象T的构造可以被看作是直接初始化全局对象T2,并且该对象的破坏将在程序退出时发生。向Thing添加move构造函数也有同样的效果,但省略的是从临时对象到t2的move构造。

邵献
2023-03-14

有关技术概述-跳到这个答案。

对于复制删除发生的常见情况-跳到此答案。

Copy elision是大多数编译器实现的一种优化,以防止在某些情况下出现额外的(潜在昂贵的)副本。它使得按值返回或按值传递在实践中是可行的(适用限制)。

这是唯一的优化形式(哈!)即使复制/移动对象有副作用,也可以应用类似规则-复制省略。

下面的例子取自维基百科:

struct C {
  C() {}
  C(const C&) { std::cout << "A copy was made.\n"; }
};
 
C f() {
  return C();
}
 
int main() {
  std::cout << "Hello World!\n";
  C obj = f();
}

根据编译器的设置,以下输出都是有效的:

你好世界!
已复制一份。
已复制一份。

你好世界!
已复制一份。

你好世界!

这也意味着可以创建更少的对象,因此您也不能依赖于被调用的特定数量的析构函数。在copy/move-constructor或destructor中不应该有关键逻辑,因为不能依赖于调用它们。

如果省略了对copy或move构造函数的调用,则该构造函数必须仍然存在并且必须可以访问。这可以确保复制删除不允许复制通常不可复制的对象,例如,因为它们有私有的或已删除的复制/移动构造函数。

C++17:从C++17开始,当直接返回对象时,保证复制省略:

struct C {
  C() {}
  C(const C&) { std::cout << "A copy was made.\n"; }
};
 
C f() {
  return C(); //Definitely performs copy elision
}
C g() {
    C c;
    return c; //Maybe performs copy elision
}
 
int main() {
  std::cout << "Hello World!\n";
  C obj = f(); //Copy constructor isn't called
}
 类似资料:
  • 有些人不知道在C中可以通过值传递和返回结构。我的问题是编译器在C中返回结构时会进行不必要的复制。C编译器(如GCC)是否使用返回值优化(RVO)优化,或者这只是C中的一个概念?我读过的所有关于RVO和复制省略的东西都是关于C的。 让我们考虑一个例子。我目前正在用C实现一个double-double数据类型(或者更确切地说是float-float开始,因为我发现它很容易进行单元测试)。考虑下面的代码

  • 我正在学习Python,我不确定这个问题是否是特定的语言,以及是如何在Python中实现的。

  • 初学者问题,很抱歉,如果这不是一个合适的地方,请尝试学习在中逻辑是如何工作的,我无法理解这一点 我希望它会说“是的!”如果是A、A、D或D,而是z,但出于某种原因,它会说“是的!”无论第一个符号是什么,都会断开,并且只检查第二个符号是否为z。

  • 本文向大家介绍什么是方法的返回值?返回值在类的方法里的作用是什么?相关面试题,主要包含被问及什么是方法的返回值?返回值在类的方法里的作用是什么?时的应答技巧和注意事项,需要的朋友参考一下 方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。 返回值的作用: 接收出结果,使得它可以用于其他的操作!

  • 下面的代码返回了一个非常奇怪的结果。它应该返回0.0008,但返回8.202154101077051E-4。有人能帮我解决这个问题吗?

  • 我正在编写自己的编程语言,由于各种原因,它编译为C。(其中之一是我对汇编知之甚少)。 我有一个关于编译器(例如GCC或Clang)如何优化从函数返回值的问题。假设我有这样的代码: 我的理解是,您可能希望变量A在从FUNC返回时复制到B(如果A和B是结构,这可能会很昂贵)。编译器会认识到在这种情况下,B可以指向a所在的位置,而不需要拷贝吗? 如果main()看起来像这样:怎么办? 谢谢大家!