10

这段代码:

#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;main 中的初始向量声明为 1,每次循环迭代为 1000。

是什么赋予了?如何从这样的函数返回向量(或任何其他对象)而不必每次都分配它?

编辑:我知道我可以通过引用传递向量。我的印象是可以(甚至更可取)编写这样的函数来返回一个对象而不会产生不必要的分配。

4

6 回答 6

23

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

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

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

这意味着 RVO/NRVO 仅在您调用函数来构造新实例时起作用,而不是在分配它时起作用。以下是可以应用 RVO/NRVO 的示例:

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

但是您的原始代码使用了一个循环,并且在每次迭代中,都getstdvec()需要构造来自的结果,并将这个临时分配给v. RVO/NRVO 无法消除这一点。

于 2013-10-18T16:13:41.917 回答
3

您可以通过引用传递它...复制省略使 v = getstdvect() 将 v (在您的 main 中)直接分配给 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);
}
于 2013-10-18T16:08:22.340 回答
2

您正在循环中进行复制分配,而不是复制构造。RVO 优化仅适用于从返回值构造变量,而不是分配给它们。

我无法完全弄清楚您要在这里解决的真正问题。有了更多详细信息,就有可能提供一个很好的答案来解决您的潜在问题。

就目前而言,要以这种方式从函数返回,您需要创建一个临时向量以在每次调用函数时返回。

于 2013-10-18T16:09:57.137 回答
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);
}
于 2013-10-18T16:09:16.170 回答
1

代替使用返回值,您可以使用参考:

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

可以避免临时对象的拷贝

于 2013-10-18T16:09:45.110 回答
1

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

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

在这种情况下,您实际上不必退回它,所以

void getstdvec(std::vector<float> &myvec){
于 2013-10-18T16:08:16.173 回答