从函数返回stl容器

时间:2011-05-13 11:07:59

标签: c++ stl containers

从函数返回stl容器的最佳方法(性能方面)是什么?返回的容器通常包含数千个项目。

方法1:

typedef std::list<Item> ItemContainer;

ItemContainer CreateManyItems() {
    ItemContainer result;

    // fill the 'result' ...

    return result;
}

ItemContainer a = CreateManyItems();

方法2:

void CreateManyItems(ItemContainer &output) {
    ItemContainer result;

    // fill the 'result' ...

    output.swap(result);
} 

ItemContainer a;
CreateManyItems(a);

方法3:

void std::auto_ptr<ItemContainer> CreateManyItems() {
    std::auto_ptr<ItemContainer> result(new ItemContainer);

    // fill the 'result' ...

    return result;
}

std::auto_ptr<ItemContainer> a = CreateManyItems();

还是有更好的方法吗?

8 个答案:

答案 0 :(得分:8)

如果您只想填充std::list项,则可以使用std::fillstd::fill_n或标准算法功能组合。

由于不清楚您希望填写列表的具体内容是什么/如何,因此无法准确评论您的代码。除非您执行使用标准算法函数无法完成的非常特殊或复杂的操作,否则请使用标准算法函数。如果它们没有帮助你,那么只有第一种方法,编译器可能会优化代码中的返回值,从而消除不必要的副本,因为大多数编译器都实现了RVO。

在维基百科上查看以下主题:

在stackoverflow本身看到这些有趣的主题:

Dave Abrahams的文章:


我仍然会强调这一点:您是否看到了<algorithm>标题提供的all the generic functions?如果没有,那么我建议你首先查看它们,看看它们中的任何一个(或它们的组合)是否可以在你的代码中做你想做的事情。

如果您想创建并填写列表,则可以使用std::generate()std::generate_n功能。

答案 1 :(得分:6)

我通常使用方法4(几乎与方法2相同):

void fill(ItemContainer& result) {
    // fill the 'result'
}

ItemContainer a;
fill(a);

答案 2 :(得分:4)

我会使用方法1并希望编译器优化掉返回值的副本。

Named Return Value Optimization

答案 3 :(得分:2)

方法1可以从复制省略中受益,但以下非常相似的代码不能:

ItemContainer a = CreateManyItems();
// do some stuff with a
a = CreateManyItems();
// do some different stuff with a

这需要C ++ 0x移动语义,以便有效地避免复制容器。当你设计你的功能时,你不知道客户将如何使用它,因此以这种方式依赖复制省略可能会让人感到不愉快。他们在C ++ 03中的修复方法如下,他们应该警惕他们需要它的情况:

ItemContainer a = CreateManyItems();
// do some stuff with a
CreateManyItems().swap(a);

由于这与方法2基本相同,因此您可以同时提供1 2作为重载,这会向调用者提示他们应该考虑潜在的性能陷阱。

如果集合中的Items没有相互引用,我首选的API形式如下,但它取决于您所暴露的接口类型 - 因为它是一个模板,实现必须在编译调用者(除非您可以提前预测它将使用的类型以及extern这些特化):

template <typename OutputIterator>
void CreateManyItems(OutputIterator out) {
     *out++ = foo; // for each item "foo"
}

ItemContainer a;
CreateManyItems(std::back_inserter(a));
// do some stuff with a
a.clear();
CreateManyItems(std::back_inserter(a));

现在,如果调用者更愿意将项目放在双端队列中,因为他们想要随机访问,那么他们只需要调整他们的代码:

std::deque<Item> a;
CreateManyItems(std::back_inserter(a));

或者如果他们根本不需要它们,他们可以编写一个流式算法,在其输出迭代器中完成它的工作,并节省大量内存。

你可以改为编写一个迭代器类,一个接一个地生成Items,但趋于更加繁琐,因为控制流是“由内而外”,并不一定会使调用者代码看起来更好(见证istream_iterator)。

答案 4 :(得分:1)

方法1没问题。它被称为复制椭圆,你会发现它自动应用于将方法1转换为方法2,基本上,但它的维护方式不那么难看。

链表?如果您甚至模糊地注重表现,请使用std::vector

答案 5 :(得分:1)

其中,排名第三的可能性能最佳。也许稍微更好,更灵活,是没有swap的数字2的变体 - 只是直接填充容器。当然,这不会是例外安全的。

答案 6 :(得分:0)

使用pimpl习语将容器封装在适当的类中。然后按值传递/返回该类。

答案 7 :(得分:0)

有一种更好的方式,我很惊讶没有一个答案指出这一点。你基本上使用输出迭代器。这使您的功能独立于容器并且更加灵活。

template<typename OutIter>
void CreateManyItems(OutIter it)
{
    //generate some data
    *it++ = 1;
    *it++ = 2;
    *it++ = 3;
}

以下是您如何使用它:

void main()
{
    //use array as output container
    int arr[3];
    CreateManyItems(arr);

    //use vector as output container
    std::vector<float> v;
    CreateManyItems(std::back_inserter(v));
}