Return 价值优化:如何避免复制构造巨大的STL容器。

Return value optimization: ho can I avoid copy construction of huge STL containers.

当我想要一个函数来 return 我一个容器时:

vector<T> func(){
    vector<T> result;
    ...
    return result; 
}

按以下方式使用:

vector<T> result = func(); 

为了避免复制我的容器的开销 我经常写这个函数,所以它 return 只是接受一个 容器的非常量实例。

void func(vector<T>& result){
    result.clear();
    ...
    result;
}

按以下方式使用:

vector<T> result;
func(result); 

难道我的努力没有意义,因为我可以确定编译器 总是使用 return 值优化?

没有意义。你说的这种RVO类型叫做named RVO(NRVO),大多数编译器都实现了。

无论如何,在 C++11 中,vector 有移动构造函数,所以即使 NRVO 不适用,它仍然会被移动,而不是被复制。

不保证 RVO,但体面的编译器会在允许的情况下使用它。

然而,问题是 RVO 仅在您在函数外部创建新对象时有帮助。如果通过引用传递来重用相同的向量,则可以利用其保留的容量来减少内存分配的次数。在函数内部创建的局部向量总是需要在内部分配一个新缓冲区,无论 return 值存储在何处。因此,通过引用传递向量会更有效,即使代码看起来不太好。

取决于编译器的年龄。在 C++11 之前,除非编译器支持命名 return 值优化——并非所有旧编译器都支持,否则您的替代方法是必需的。此外,您还可以让函数 return 引用传递的向量。

从 C++11 开始,该语言支持移动构造,并且标准容器具有有效的移动构造函数,因此您的第一种方法很好。纯粹主义者会坚持认为更好。实用主义者(他们意识到并非每个人都可以在不付出巨大代价的情况下更新他们的编译器)会说根据您的代码是否需要继续使用 C++11 之前和更高版本的编译器的混合来选择解决方案。

我已经用 gcc 试过了。我意识到在没有 C++11 标志的情况下编译时我不能依赖 NRVO。

因为我不喜欢第二个签名(函数通过引用获取容器)所以我想出了这个:

以自然形式声明函数:

vector<T> func(){
    vector<T> result;
    ...
    return result; 
}

并且,当我不确定编译器和编译标志时,可以这样使用它:

vector<T> result;
func().swap(result)

通过这种方式可以获得所需的界面,并且一定会避免不必要的开销。

请注意,result 向量的容量是函数返回的向量之一。如果要设置向量的容量,则该函数的正确接口是第二个。