此代码:
#include <vector>
std::vector<float> getstdvec() {
std::vector<float> v(4);
v[0] = 1;
v[1] = 2;
v[2] = 3;
v[3] = 4;
return v;
}
int main() {
std::vector<float> v(4);
for (int i = 0; i != 1000; ++i)
{
v = getstdvec();
}
}
我在这里的错误理解是函数getstdvec不应该实际分配它返回的向量。当我在valgrind / callgrind中运行它时,我看到有1001个调用malloc; 1表示main中的初始向量声明,1000表示每次循环迭代。
是什么给出的?如何从这样的函数返回向量(或任何其他对象)而不必每次都分配它?
编辑:我知道我可以通过引用传递向量。我的印象是,有可能(甚至更可取)编写一个这样的函数来返回一个对象而不会产生不必要的分配。
答案 0 :(得分:17)
当您调用函数时,对于std::vector<T>
这样的返回类型,编译器会为返回的对象提供内存。被调用的函数负责构造它在这个内存槽中返回的实例。
RVO / NRVO现在允许编译器省略创建本地临时对象,从中复制构造内存槽中的返回值,破坏临时对象并最终返回给调用者。相反,被调用的函数只是直接在返回槽的内存中构造本地对象,而在函数的末尾,它只是返回。
从调用者的角度来看,这是透明的:它为返回的值提供内存,当调用的函数返回时,有一个有效的实例。调用者现在可以使用此对象,并负责调用析构函数并在以后释放内存。
这意味着RVO / NRVO仅在您调用函数构造新实例时工作,而不是在您指定它时。以下是可以应用RVO / NRVO的示例:
std::vector<float> v = getstdvec();
但是原始代码使用循环,并且在每次迭代中,需要构造getstdvec()
的结果,并将此临时值分配给v
。 RVO / NRVO无法将其删除。
答案 1 :(得分:3)
你可以通过引用传递它... copy elision使它成为v = getstdvect()将v(在你的主中)直接分配给v(在你的getstdvec()中)并跳过通常与返回相关联的副本值,但它不会跳过函数中的v(4)。为此,您需要通过引用获取向量:
#include <vector>
void getstdvec(std::vector<float>& v){
v.resize(4);//will only realocate if v is wrong size
v[0] = 1; v[1] = 2; v[2] = 3; v[3] = 4;
return v;
}
int main() {
std::vector<float> v(4);
for (int i=0; i!=1000;++i)
getstdvec(v);
}
答案 2 :(得分:2)
你在循环中进行复制赋值,而不是复制构造。 RVO优化仅适用于从返回值构造变量,而不是分配给它们。
我无法弄清楚你在这里要解决的真正问题。通过更多细节,可以提供解决潜在问题的良好答案。
就目前而言,要以这种方式从函数返回,您需要创建一个临时向量,以便在每次调用函数时返回。
答案 3 :(得分:1)
最简单的答案是将已创建的矢量对象传递给函数。
std::vector<float> getstdvec(std::vector<float> &myvec){
在这种情况下,你真的不必将其归还
void getstdvec(std::vector<float> &myvec){
答案 4 :(得分:1)
如何从这样的函数返回向量(或任何其他对象)而不必每次都分配它?
以你的方式,你声明一个大小为4的本地向量,所以每次调用该函数时,它都会分配内存。如果你的意思是你总是修改同一个向量,那么你可以考虑通过引用传递向量。
例如:
void getstdvec(std::vector<float>& vec)
{ //^^
//do something with vec
}
在main
内,你声明了向量并按照你的方式分配空间。您现在执行以下操作:
for (int i=0; i!=1000;++i)
{ //^^^minor: Don't use magic number in the code like this,
//define a const instead
getstdvec(vec);
}
答案 5 :(得分:1)
替代使用返回值,可以使用引用:
void getstdvec(std::vector<float> &v)
可以避免临时对象的复制