在c ++中返回std :: vector的有效方法

Mor*_*ten 71 c++ return-value stdvector

在函数中返回std :: vector时复制了多少数据,将std :: vector放在free-store(在堆上)并返回指针的优化程度是多少,即:

std::vector *f()
{
  std::vector *result = new std::vector();
  /*
    Insert elements into result
  */
  return result;
} 
Run Code Online (Sandbox Code Playgroud)

效率高于:

std::vector f()
{
  std::vector result;
  /*
    Insert elements into result
  */
  return result;
} 
Run Code Online (Sandbox Code Playgroud)

Naw*_*waz 102

在C++ 11中,这是首选方式:

std::vector<X> f();
Run Code Online (Sandbox Code Playgroud)

也就是说,按价值返回.

对于C++ 11,它std::vector具有移动语义,这意味着函数中声明的局部向量将在返回时移动,在某些情况下甚至可以由编译器省略移动.

  • @LeonidVolnitsky:是的,如果是*local*.实际上,`return std :: move(v);`将禁用move-elision,即使只有`return v;`也是如此.所以后者是首选. (10认同)
  • 即使没有`std::move`,它也会被移动吗? (3认同)
  • @juanchopanza:我不这么认为。在 C++11 之前,你可能会反对它,因为向量不会被移动;而RVO是一个依赖于编译器的东西!谈论80、90年代的事情。 (2认同)
  • 我对返回值(按值)的理解是:被调用者中的返回值是在调用者的堆栈上创建的,而不是“被移动”,因此被调用者中的所有操作都是就地的,RVO 中没有什么可移动的. 那是对的吗? (2认同)
  • @r0ng:是的,确实如此。这就是编译器通常如何实现 RVO。 (2认同)

Ste*_*sop 49

你应该按价值回报.

该标准具有特定的功能,以提高按价值返回的效率.它被称为"复制省略",更具体地说是"命名返回值优化(NRVO)".

编译器没有实现它,但随后又编译器不具备实现内联函数(或执行任何优化的话).但是,如果编译器没有进行优化,标准库的性能可能会非常差,并且所有严格的编译器都会实现内联和NRVO(以及其他优化).

应用NRVO时,以下代码中不会复制:

std::vector<int> f() {
    std::vector<int> result;
    ... populate the vector ...
    return result;
}

std::vector<int> myvec = f();
Run Code Online (Sandbox Code Playgroud)

但是用户可能想要这样做:

std::vector<int> myvec;
... some time later ...
myvec = f();
Run Code Online (Sandbox Code Playgroud)

复制省略不会阻止复制,因为它是一个赋值而不是初始化.但是,您应按值返回.在C++ 11中,赋值通过不同的东西进行优化,称为"移动语义".在C++ 03中,上面的代码确实会产生副本,虽然理论上优化器可能能够避免它,但实际上它太难了.所以myvec = f()在C++ 03中你应该这样写:

std::vector<int> myvec;
... some time later ...
f().swap(myvec);
Run Code Online (Sandbox Code Playgroud)

还有另一种选择,即为用户提供更灵活的界面:

template <typename OutputIterator> void f(OutputIterator it) {
    ... write elements to the iterator like this ...
    *it++ = 0;
    *it++ = 1;
}
Run Code Online (Sandbox Code Playgroud)

然后,您还可以支持现有的基于矢量的界面:

std::vector<int> f() {
    std::vector<int> result;
    f(std::back_inserter(result));
    return result;
}
Run Code Online (Sandbox Code Playgroud)

如果现有代码的使用方式比预先固定的数量更复杂,那么这可能比现有代码效率低reserve().但是如果您现有的代码基本上push_back反复调用向量,那么这个基于模板的代码应该是一样好的.