C++ 从C+;中的函数调用返回std::vector的正确方法(移动语义)+;11

C++ 从C+;中的函数调用返回std::vector的正确方法(移动语义)+;11,c++,stl,c++11,move-semantics,return-value-optimization,C++,Stl,C++11,Move Semantics,Return Value Optimization,我想填充std::vector(或其他STL容器): class-Foo{ 公众: Foo(整数、常数条和常数); 私人: std::矢量食物; } 1.外形美观,性能昂贵 std::vector get\u vector(int\u n,const Bar&\u m){ std::载体ret; …//根据参数填充ret 返回ret; } Foo::Foo(int _n,const Bar&_m):fooes(get _vector(_n,_m){} 2.性能更好,外观更差 void fil

我想填充std::vector(或其他STL容器):

class-Foo{
公众:
Foo(整数、常数条和常数);
私人:
std::矢量食物;
}
1.外形美观,性能昂贵

std::vector get\u vector(int\u n,const Bar&\u m){
std::载体ret;
…//根据参数填充ret
返回ret;
}
Foo::Foo(int _n,const Bar&_m):fooes(get _vector(_n,_m){}
2.性能更好,外观更差

void fill\u vector(整数、常数条和常数、标准::vector和ret){
…//根据参数填充ret
}
Foo::Foo(int _n,const Bar&_m){fill_向量(_n,_m,fooes)}

是否可以使用C++0x重写第一个示例中的
get_vector
函数(移动语义功能等)为了避免重复的复制和构造函数调用?

如果您使用的是与C++0x兼容的编译器和标准库,则可以在不做任何操作的情况下从第一个示例获得更好的性能。
get\u vector(\n,\m)
的返回值是临时的,而
std::vector
(采用右值引用的构造函数)将自动调用,而无需您进行进一步的工作


一般来说,非库编写者不需要直接使用右值引用;您只需要自动获得一大块好处。

在您考虑的特定情况下,第一个实现与第二个实现一样高效。编译器将在
get\u vector
中优化
ret
的副本n操作返回值,它将使用移动语义将向量的所有权转移到容器类。向量中的移动构造需要(依赖于实现,但需要良好的近似值)3个指针副本,与容器中元素的数量和大小无关。将向量作为要修改的引用传递需要一个指针副本(再次计算近似成本),但对向量执行的任何操作都将支配任一选项的成本

在将向量传递到函数中进行修改时,有一些非常特殊的情况可能会更快,但这些情况很少发生,而且与域的关系比与向量本身的关系更大。请忽略这些情况,首先编写代码以确保可维护性,如果程序运行缓慢,请分析程序,确定程序的成本,然后再考虑优化。有趣的是,一旦你分析过,你可能知道瓶颈是什么,这意味着你会有一些改变的提示。

我相信(1)和(2)即使没有C++0x,也有相同的性能,只要编译器执行命名返回值优化,我相信大多数编译器都会这样做。既不应该执行任何复制,也不应该执行任何移动


如果我错了,请纠正我,因为如果我误解了NRVO。

关于性能,这取决于:如果您编写类似于
std::vector v(get_vector(…)
,编译器应该能够优化复制。如果您这样做
v=get_vector(…)
在现有的
std::vector上,可能不是。@James Kanze:这就是注释的根本原因某些特定情况下传递向量可能会更快。执行
clear()
在容器中不会释放内存,如果新元素放在同一个容器中,则也不需要获取内存,因此效率更高。但是谈论对象创建(如问题中的成员初始化),它们将是等价的。@David Rodriguez或不是。唯一确定的方法是尝试这两种方法,并进行测量(在真正出现性能问题之前,不值得费心).在一两种特殊情况下,我发现将变量移出循环会更快,因为您提到的原因是:一旦分配了内存,它就不会被释放,因此您最终会减少重新分配。(但这不是一般情况。)我的建议是只返回容器,直到它成为一个性能问题,然后尝试不同的解决方案,以找到一个解决问题的方法。@James:如果你这样做,
v=get\u vector(…)
在现有的
std::vector
上,没有副本;此调用的
vector
的移动赋值运算符,因为
get_vector
返回一个右值。我看不出你在说什么…@ildjarn不使用我的编译器,它不使用。大多数编译器都不提供移动语义,我使用的编译器在标准库,即使它们可用。您能否澄清这些函数是否会修改您的
\u m
参数?这些函数中的
\u m
是如何处理的?它是否复制到
get\u vector
中?@Johannes-感谢您删除的答案(+1)。我添加了两个不同的参数只是为了说明。我对它们的性质不太感兴趣。你是对的,最好添加一个常量,以免引起混淆。我不太理解右值引用。是不是
Bar
必须在
Foo::Foo(int\n,Bar\m)fooes\get\u vector(\n,move(\m)){
?这里没有描述RVO、移动等的所有细节。这里有一个链接,指向这篇文章的详细信息:这是正确的。即使在C++0x中,编译器也会首先考虑RVO,如果不能,它会考虑移动对象,如果不允许,它会作为最后手段进行复制。
class Foo {
public:
  Foo(int _n, const Bar &_m);
private:
  std::vector<Foo> fooes_;
}
std::vector<Foo> get_vector(int _n, const Bar &_m) {
  std::vector<Foo> ret;
  ... // filling ret depending from arguments
  return ret;
}

Foo::Foo(int _n, const Bar &_m) : fooes_(get_vector(_n, _m) {}
void fill_vector(int _n, const Bar &_m, std::vector<Foo> &_ret) {
  ... // filling ret depending from arguments
}

Foo::Foo(int _n, const Bar &_m) { fill_vector(_n, _m, fooes_); }