什么是复制省略和返回值优化?
什么是复制elision? 什么是(命名)返回值优化? 他们意味着什么?
在什么情况下可以发生? 什么是限制?
介绍
技术概述 - 跳到这个答案。
对于出现复制瑕疵的常见情况 - 跳到此答案。
复制elision是由大多数编译器实施的优化措施,以防止在某些情况下额外(可能是昂贵的)副本。 它使价值回归或价值传递在实践中可行(限制适用)。
这是唯一的优化形式, 即使复制/移动对象具有副作用 ,elides(ha!)as-if规则 - 复制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 elision不允许复制通常不可复制的对象,例如因为它们具有私有或已删除的复制/移动构造函数。
标准参考
对于较少技术的观点和介绍 - 跳到这个答案。
对于出现复制瑕疵的常见情况 - 跳到此答案。
复制elision在标准中定义如下:
12.8复制和移动类对象[class.copy]
如
31)当满足某些标准时,即使对象的复制/移动构造函数和/或析构函数具有副作用,也允许实现省略类对象的复制/移动构造。 在这种情况下,实现将被忽略的复制/移动操作的来源和目标视为简单地引用同一对象的两种不同方式,并且对象的销毁发生在两个对象将在在没有优化的情况下被销毁.123在下列情况下(这可以合并以消除多个副本),允许复制/移动操作的缩写(称为复制删除):
- 在具有类返回类型的函数的返回语句中,当表达式是具有与函数返回类型相同的cvun限定类型的非易失性自动对象(函数或catch-clause参数除外)的名称时,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作
- 在操作数是一个非易失性自动对象(函数或catch-clause参数除外)的名称时,它的作用域未超出最内层的try-block的末尾(如果存在一个),通过将自动对象直接构造到异常对象中,可以省略从操作数到异常对象(15.1)的复制/移动操作
- 当未绑定到引用(12.2)的临时类对象将被复制/移动到具有相同cv-unqualified类型的类对象时,可以通过将临时对象直接构造到省略复制/移动的目标
- 当异常处理程序(第15章)的异常声明声明与异常对象(15.1)相同类型的对象(cv-qualification除外)时,可以通过处理异常声明来省略复制/移动操作作为异常对象的别名,如果除异常声明声明的对象的构造函数和析构函数执行外,程序的含义将保持不变。
123)因为只有一个对象被破坏而不是两个,并且一个复制/移动构造函数没有被执行,所以每个构造对象仍然有一个对象被销毁。
给出的例子是:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
并解释说:
这里elision的标准可以合并,以消除对Thing
类的拷贝构造函数的两次调用:将本地自动对象t
复制到函数f()
的返回值的临时对象中,并将该临时对象复制到对象中t2
。 实际上,本地对象t
的构造可以被看作是直接初始化全局对象t2
,并且该对象的销毁将在程序出口处发生。 向Thing添加一个移动构造函数具有相同的效果,但它是从临时对象到t2
的移动构造被消除。
常见的复制形式
技术概述 - 跳到这个答案。
对于较少技术的观点和介绍 - 跳到这个答案。
(命名)返回值优化是复制elision的常见形式。 它指的是根据方法的值返回的对象将其副本消除的情况。 标准中列出的示例说明了命名的返回值优化 ,因为该对象已被命名。
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
定期返回值优化发生在临时返回时:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
return Thing();
}
Thing t2 = f();
其他发生复制冲突的常见场所是临时性按价值传递的情况 :
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
void foo(Thing t);
foo(Thing());
或者当一个异常被抛出并被值捕获时 :
struct Thing{
Thing();
Thing(const Thing&);
};
void foo() {
Thing c;
throw c;
}
int main() {
try {
foo();
}
catch(Thing c) {
}
}
复制省略的常见限制是:
大多数商业级编译器都支持copy elision和(N)RVO(取决于优化设置)。
链接地址: http://www.djcxy.com/p/72909.html上一篇: What are copy elision and return value optimization?
下一篇: >, .* (C++)