6

当我想要一个函数返回一个容器时:

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

可按以下方式使用:

vector<T> result = func(); 

为了避免复制我的容器的开销,我经常编写函数,以便它只返回一个容器的非常量实例。

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

可按以下方式使用:

vector<T> result;
func(result); 

我的努力是否毫无意义,因为我可以确定编译器总是使用返回值优化?

4

4 回答 4

10

这是没有意义的。您提到的 RVO 类型称为命名 RVO (NRVO),大多数编译器都实现了它。

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

于 2015-11-06T08:53:36.470 回答
2

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

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

于 2015-11-06T08:56:38.717 回答
2

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

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

于 2015-11-06T08:59:09.130 回答
0

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

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

以自然形式声明函数:

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

并且,当我不确定编译器和编译标志时,请以这种方式使用它:

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

通过这种方式,可以获得所需的接口,并确保避免可忽略的开销。

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

于 2015-11-07T19:21:25.023 回答