在2016年奥卢ISO C++标准会议上,一个名为通过简化值类别实现保证复制省略的提案被标准委员会投票纳入C++17标准。
保证复制省略如何工作?它是否涵盖了已经允许复制省略的一些情况,还是需要代码更改才能保证复制省略?
保证复制省略如何工作?它是否涵盖了已经允许复制省略的一些情况,还是需要代码更改才能保证复制省略?
复制省略在许多情况下是被允许的。但即使被允许,代码仍然必须能够像没有省略复制一样工作。也就是说,必须有一个可访问的复制和/或移动构造函数。
保证复制省略重新定义了许多C++概念,以便某些情况下可以省略复制/移动,实际上根本不会引发复制/移动。编译器不是省略了一个复制;标准规定根本不会发生任何复制。
考虑这个函数:
T Func() {return T();}
根据非保证拷贝省略规则,这将创建一个临时对象,然后从该临时对象移动到函数的返回值中。该移动操作有可能被省略,但即使未被使用,T
仍必须具有可访问的移动构造函数。
类似地:
T t = Func();
t
的复制初始化。它将使用Func
的返回值来进行t
的复制初始化。然而,即使不会调用移动构造函数,T
仍然必须具有移动构造函数。return T();
时,这将通过prvalue初始化函数的返回值。由于该函数返回T
,因此不会创建任何临时对象; prvalue的初始化只是直接初始化返回值。T()
一样。T t = Func();
时,返回值的prvalue直接初始化对象t
;没有“创建临时对象并复制/移动”的阶段。由于Func()
的返回值是等价于T()
的prvalue,因此t
由T()
直接初始化,就像您执行T t = T()
一样。const T &rt = Func();
,则prvalue将实例化一个临时对象(使用T()
作为初始化程序),其引用将与通常的临时生命周期延长一起存储在rt
中。lock_guard
无法复制或移动,因此您无法通过值返回它的函数。但是,在保证的复制省略下,您可以这样做。new T(FactoryFunction());
FactoryFunction
通过值返回T
,这个表达式将不会将返回值复制到分配的内存中。相反,它将分配内存并直接使用已分配的内存作为函数调用的返回值内存。T
的prvalue。new auto(FactoryFunction());
如果你不喜欢写类型名称。
需要注意的是,上述保证仅适用于prvalues。也就是说,在返回已命名变量时,您没有任何保证:
T Func()
{
T t = ...;
...
return t;
}
t
仍然必须具有可访问的复制/移动构造函数。是的,编译器可以选择优化掉复制/移动操作。但编译器仍然必须验证可访问的复制/移动构造函数的存在。#include <iostream>
using namespace std;
class Foo {
public:
Foo() {cout << "Foo constructed" << endl; }
Foo(const Foo& foo) {cout << "Foo copy constructed" << endl;}
Foo(const Foo&& foo) {cout << "Foo move constructed" << endl;}
~Foo() {cout << "Foo destructed" << endl;}
};
Foo fReturnValueOptimization() {
cout << "Running: fReturnValueOptimization" << endl;
return Foo();
}
Foo fNamedReturnValueOptimization() {
cout << "Running: fNamedReturnValueOptimization" << endl;
Foo foo;
return foo;
}
int main() {
Foo foo1 = fReturnValueOptimization();
Foo foo2 = fNamedReturnValueOptimization();
}
vinegupt@bhoscl88-04(~/progs/cc/src)$ g++ -std=c++11 testFooCopyElision.cxx # Copy elision enabled by default
vinegupt@bhoscl88-04(~/progs/cc/src)$ ./a.out
Running: fReturnValueOptimization
Foo constructed
Running: fNamedReturnValueOptimization
Foo constructed
Foo destructed
Foo destructed
vinegupt@bhoscl88-04(~/progs/cc/src)$ g++ -std=c++11 -fno-elide-constructors testFooCopyElision.cxx # Copy elision disabled
vinegupt@bhoscl88-04(~/progs/cc/src)$ ./a.out
Running: fReturnValueOptimization
Foo constructed
Foo move constructed
Foo destructed
Foo move constructed
Foo destructed
Running: fNamedReturnValueOptimization
Foo constructed
Foo move constructed
Foo destructed
Foo move constructed
Foo destructed
Foo destructed
Foo destructed
std::function<T()>
。 - Yakk - Adam Nevraumont