2013-10-18 59 views
9

此代码:C++返回值优化

#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

对于您的编辑:我们需要一个您正试图解决的问题的实例,而不是这个极其微小的示例代码,以帮助提供非通过参考的解决方案。 –

+0

@MarkB,它的确如此简单:我想要一个返回矢量而不必进行不必要的复制/分配的函数。我的印象是,与RVO或rvalues有关的东西会使这件事情变得非常简单。一个简单的现实世界的例子就是尝试对向量y和x以及标量k做y = k * x。传统的传递参考函数看起来像'void mult(const float&k,const vec&x,vec&y)'。但显然函数调用'y = mult(k,x)'比'mult(k,x,y)'更可取。 – Aurelius

+1

RVO(返回值优化)是编译器对您的代码执行的操作。你的代码需要首先做一些可以优化的东西(例如传递一个临时的东西,例如分配给同一个对象)。你可能已经看过那个代码并且认为 - 嗯,我可以通过传递一个对getstdvec的引用来优化它。为什么编译器不这样做?那么,传递参考并不代表你的代码。你只能期望编译器优化你的代码所做的事情 - 而不是它可以做的事情。 – iheanyi

回答

1

最简单的答案是将已创建的矢量对象传递给函数。

std::vector<float> getstdvec(std::vector<float> &myvec){ 

在这种情况下,你真的没有返回这么

void getstdvec(std::vector<float> &myvec){ 
3

您可以通过引用传递它...复制省略使得它使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); 
} 
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); 
} 
1

在代替使用返回值,你可以使用一个参考:

void getstdvec(std::vector<float> &v) 

可避免临时对象

2

你做副本的副本在循环中分配,而不是复制。 RVO优化仅适用于从返回值构造变量,而不是分配给它们。

我不能完全弄清楚你在这里试图解决的真正问题。有了更多的细节,就有可能提供一个解决您的潜在问题的良好答案。按照这种方式返回函数,您需要创建一个临时向量,以在每次调用该函数时返回。

15

当您调用函数时,对于返回类型如std::vector<T>,编译器会为返回的对象提供内存。被调用的函数负责构造它在这个内存槽中返回的实例。

RVO/NRVO现在允许编译器省略创建本地临时对象,从内存槽中复制构造返回值,破坏临时对象并最终返回给调用者。相反,被调用函数直接在返回槽的内存中直接构造本地对象,并在函数结束时返回。

从调用者的角度来看,这是透明的:它为返回的值提供内存,并且当调用的函数返回时,会有一个有效的实例。调用者现在可以使用这个对象,并且负责调用析构函数并在以后释放内存。

这意味着RVO/NRVO仅适用于当您调用函数构造新实例时,而不是在您分配它时。以下是其中可以应用RVO/NRVO一个例子:

std::vector<float> v = getstdvec(); 

但原始代码使用循环和在每次迭代中,需要构建从getstdvec()结果和该临时分配给v。 RVO/NRVO无法解决这个问题。