Vin*_*ent 34 c++ copy return object return-value
我在C++中有一个非常基本的问题.返回对象时如何避免复制?
这是一个例子:
std::vector<unsigned int> test(const unsigned int n)
{
std::vector<unsigned int> x;
for (unsigned int i = 0; i < n; ++i) {
x.push_back(i);
}
return x;
}
Run Code Online (Sandbox Code Playgroud)
据我了解C++是如何工作的,这个函数将创建2个向量:本地一个(x),以及将返回的x的副本.有没有办法避免副本?(我不想返回指向对象的指针,但是对象本身)
使用"移动语义"(在评论中说明)该函数的语法是什么?
Mat*_* M. 38
关于RVO(返回值优化)如何工作似乎存在一些混淆.
一个简单的例子:
#include <iostream>
struct A {
int a;
int b;
int c;
int d;
};
A create(int i) {
A a = {i, i+1, i+2, i+3 };
std::cout << &a << "\n";
return a;
}
int main(int argc, char*[]) {
A a = create(argc);
std::cout << &a << "\n";
}
Run Code Online (Sandbox Code Playgroud)
它的输出在ideone:
0xbf928684
0xbf928684
Run Code Online (Sandbox Code Playgroud)
令人惊讶?
实际上,这就是RVO的影响:要返回的对象直接在调用者中构建.
怎么样 ?
传统上,调用者(main
这里)将在堆栈上为返回值保留一些空间:返回槽 ; 被调用者(create
这里)以某种方式传递返回槽的地址以将其返回值复制到.然后,被调用者为其构建结果的局部变量分配自己的空间,就像任何其他局部变量一样,然后将其复制到return
语句的返回槽中.
当编译器从代码中推断出变量可以直接构造到具有等效语义的返回槽(as-if规则)时触发RVO .
请注意,这是一种常见的优化,它由标准明确列入白名单,编译器不必担心复制(或移动)构造函数可能产生的副作用.
什么时候 ?
编译器最有可能使用简单的规则,例如:
// 1. works
A unnamed() { return {1, 2, 3, 4}; }
// 2. works
A unique_named() {
A a = {1, 2, 3, 4};
return a;
}
// 3. works
A mixed_unnamed_named(bool b) {
if (b) { return {1, 2, 3, 4}; }
A a = {1, 2, 3, 4};
return a;
}
// 4. does not work
A mixed_named_unnamed(bool b) {
A a = {1, 2, 3, 4};
if (b) { return {4, 3, 2, 1}; }
return a;
}
Run Code Online (Sandbox Code Playgroud)
在后一种情况(4)中,A
返回时不能应用优化,因为编译器无法a
在返回槽中构建,因为它可能需要其他东西(取决于布尔条件b
).
因此,一个简单的经验法则是:
如果在声明之前没有声明返回槽的其他候选者,则应该应用RVO return
.
Pub*_*bby 18
该程序可以利用命名的返回值优化(NRVO).见这里:http://en.wikipedia.org/wiki/Copy_elision
在C++ 11中,有移动构造函数和赋值也很便宜.您可以在这里阅读教程:http://thbecker.net/articles/rvalue_references/section_01.html
Lih*_*ihO 14
命名返回值优化将为您完成工作,因为编译器在使用它时尝试消除冗余的Copy构造函数和Destructor调用.
std::vector<unsigned int> test(const unsigned int n){
std::vector<unsigned int> x;
return x;
}
...
std::vector<unsigned int> y;
y = test(10);
Run Code Online (Sandbox Code Playgroud)
具有返回值优化:
(如果你想亲自尝试更深入了解,请看看我的这个例子)
甚至更好,就像Matthieu M.指出的那样,如果你test
在y
声明的同一行内调用,你也可以避免构造冗余对象和冗余赋值(x
将在内存中构建y
将被存储):
std::vector<unsigned int> y = test(10);
Run Code Online (Sandbox Code Playgroud)
检查他的答案以更好地理解这种情况(你也会发现不能总是应用这种优化).
或者你可以修改你的代码,将vector的引用传递给你的函数,这在语义上更正确,同时避免复制:
void test(std::vector<unsigned int>& x){
// use x.size() instead of n
// do something with x...
}
...
std::vector<unsigned int> y;
test(y);
Run Code Online (Sandbox Code Playgroud)