简体   繁体   English

函数返回C ++中的对象集合

[英]Functions returning a collection of objects in C++

In my current project I need to implement quite a few functions/methods that take some parameters and generate a collection of results (rather large). 在我目前的项目中,我需要实现相当多的函数/方法,这些函数/方法需要一些参数并生成一组结果(相当大)。 So in order to return this collection without copying, I can either create a new collection and return a smart pointer: 因此,为了在不复制的情况下返回此集合,我可以创建一个新集合并返回一个智能指针:

boost::shared_ptr<std::vector<Stuff> > generate();

or take a reference to a vector which will be populated: 或者引用一个将被填充的向量:

void generate(std::vector<Stuff> &output);

Both approaches have benefits. 这两种方法都有好处。 The first clearly shows that the vector is the output of the function, it is trivial to use in a parallelized scenario, etc. The second might be more efficient when called in a loop (because we don't allocate memory every time), but then it is not that obvious that the parameter is the output, and someone needs to clean the old data from the vector... 第一个清楚地表明向量是函数的输出,在并行场景中使用它是微不足道的。第二个可能在循环中调用时更有效(因为我们不是每次都分配内存),但是那么参数是输出并不明显,有人需要从矢量中清除旧数据......

Which would be more customary in real life (ie what is the best practise)? 在现实生活中哪些更习惯(即什么是最佳做法)? In C#/java I would argue that the first one, what is the case in C++? 在C#/ java中我会争论第一个,C ++中的情况是什么?

Also, is it possible to effectively return a vector by value using C++11? 此外,是否可以使用C ++ 11有效地按值返回向量? What would the pitfalls be? 会有什么陷阱?

do correctness first, then optimize if necessary 首先做正确性, 然后在必要时进行优化

with both move semantics and Return Value Optimization conspiring to make an ordinary function result non-copying, you would probably have to work at it to make it sufficiently inefficient to be worth optimization work 同时使用移动语义和返回值优化共谋使普通函数结果不复制,您可能必须使用它来使其足够低效,值得优化工作

so, just return the collection as a function result, then MEASURE if you feel that it's too slow 所以,只需将集合作为函数结果返回,然后测量它是否感觉它太慢了

You should return by value. 你应该按价值回报。

is it possible to effectively return a vector by value using C++11? 是否可以使用C ++ 11有效地按值返回向量?

Yes, C++11 supports move semantics . 是的,C ++ 11支持移动语义 You return a value, but the compiler knows it's a temporary, and therefore can invoke a special constructor (move constructor) that is especially designed to simply "steal the guts" of the returned object. 您返回一个值,但编译器知道它是临时的,因此可以调用一个特殊的构造函数(移动构造函数),该构造函数专门用于简单地“窃取返回对象的内容”。 After all, you won't use that temporary object anymore, so why copying it when you can just move its content? 毕竟,你不再使用那个临时对象了,那么为什么要在你移动它的内容时复制呢?

Apart from this, it may be worth mentioning that most C++ compilers, even pre-C++11, implement (Named) Return Value Optimization , which would elide the copy anyway, incurring in no overhead. 除此之外,值得一提的是,大多数C ++编译器,甚至是前C ++ 11,实现(命名)返回值优化 ,无论如何都会忽略副本,不会产生任何开销。 Thus, you may want to actually measure the performance penalty you (possibly) get before optimizing. 因此,您可能希望实际测量在优化之前(可能)获得的性能损失。

I think you should pass by reference, or return a shared pointer, only when you need reference semantics . 我认为只有在需要引用语义时才应该通过引用传递,或者返回共享指针。 This does not seem to be your case. 这似乎不是你的情况。

There is an alternative approach. 还有另一种方法。 If you can make your functions template, make them take an output iterator (whose type is a template argument) as argument: 如果你可以创建函数模板,那么让它们将输出迭代器(其类型是模板参数)作为参数:

tempalte<class OutputIterator>
void your_algorithm(OutputIterator out) {
    for(/*condition*/) {
        ++out = /* calculation */;
    }
}

This has the advantage that the caller can decide in what kind of collection he wants to store the result (the output iterator could for instance write directly to a file, or store the result in a std::vector , or filter it, etc.). 这样做的好处是调用者可以决定他想要存储结果的集合类型(输出迭代器可以直接写入文件,或者将结果存储在std::vector ,或者过滤它等等)。 )。

The best practise will probably be surprising to you. 最佳实践可能会让您感到惊讶。 I would recommend returning by value in both C++03 and C++11. 我建议在C ++ 03和C ++ 11中按值返回。

  • In C++03, if you create a std::vector local to generate and return it, the copy may be elided by the compiler (and almost certainly will be). 在C ++ 03中,如果你创建一个std::vector local来generate并返回它,那么编译器可能会省略该副本(几乎可以肯定)。 See C++03 §12.8/15: 见C ++03§12.8/ 15:

    in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy operation can be omitted by constructing the automatic object directly into the function's return value 在具有类返回类型的函数的return语句中,当表达式是具有与函数返回类型相同的cv-nonqualified类型的非易失性自动对象的名称时,可以通过构造自动对象来省略复制操作直接进入函数的返回值

  • In C++11, if you create a std::vector local to generate and return it, the copy will first be considered as a move first (which will already be very fast) and then that may be elided (and almost certainly will be). 在C ++ 11,如果你创建std::vector本地generate并返回,副本将首先被视为移动第一(这会已经是非常快的),然后可以被省略(并且几乎肯定会是)。 See C++11 §12.8/31: 见C ++11§12.8/ 31:

    in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value 在具有类返回类型的函数的return语句中,当表达式是具有与函数返回类型相同的cv-unqualified类型的非易失性自动对象(函数或catch子句参数除外)的名称时,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作

    And §12.8/32: §12.8/ 32:

    When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. 当满足或将满足复制操作的省略标准时,除了源对象是函数参数这一事实,并且要复制的对象由左值指定,重载决策选择复制的构造函数是首先执行,好像对象是由右值指定的。

So return by value! 所以按价值回报!

Believe it or not, I'm going to suggest that instead of either of those approaches just take the obvious implementation and return by value! 信不信由你,我建议不要采取任何一种方法,只需采取明显的实施方式,并按价值回报! Compilers are very often able to optimize away the notional copy that would be induced, removing it completely. 编译器通常能够优化掉所引发的名义副本,将其完全删除。 By writing the code in the most obvious manner you make it very clear to future maintainers what the intent is. 通过以最明显的方式编写代码,您可以非常清楚未来的维护者意图是什么。

But let's say you try return by value and your program runs too slow and let's further suppose that your profiler shows that the return by value is in fact your bottleneck. 但是,假设您尝试按值返回,并且您的程序运行速度太慢让我们进一步假设您的分析器显示按值返回实际上是您的瓶颈。 In this case I would allocate the container on the heap and return as an auto_ptr in C++03 or a unique_ptr in C++11 to clearly indicate that ownership is being transferred and that the generate isn't keeping a copy of that shared_ptr for its own purposes later. 在这种情况下,我将在堆上分配容器并作为C ++ 03中的auto_ptr或C ++ 11中的unique_ptr返回,以清楚地指示所有权正在转移,并且生成不保留该shared_ptr的副本出于以后的目的。

Finally, the series at http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ provides a great perspective on almost the exact same question. 最后, http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/上的系列提供了几乎完全相同的问题的一个很好的视角。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM