2

在我当前的项目中,我需要实现相当多的函数/方法,这些函数/方法采用一些参数并生成一组结果(相当大)。因此,为了在不复制的情况下返回这个集合,我可以创建一个新集合并返回一个智能指针:

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

或引用将被填充的向量:

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

这两种方法都有好处。第一个清楚地表明向量是函数的输出,在并行化场景中使用它是微不足道的,等等。第二个在循环中调用时可能更有效(因为我们不是每次都分配内存),但是那么参数是输出就不是那么明显了,需要有人从向量中清除旧数据...

在现实生活中哪个更习惯(即最佳做法是什么)?在 C#/java 中,我认为第一个,在 C++ 中是什么情况?

此外,是否可以使用 C++11 按值有效地返回向量?会有什么陷阱?

4

5 回答 5

4

先做正确性,然后在必要时进行优化

由于移动语义和返回值优化共同使普通函数结果不可复制,您可能必须努力使其效率低到值得优化工作

所以,只需将集合作为函数结果返回,然后MEASURE如果你觉得它太慢了

于 2013-02-21T14:09:49.713 回答
2

你应该按价值返回。

是否可以使用 C++11 按值有效地返回向量?

是的,C++11 支持移动语义。您返回一个值,但编译器知道它是一个临时值,因此可以调用一个特殊的构造函数(移动构造函数),该构造函数专门设计用于简单地“窃取”返回对象的内容。毕竟,你不会再使用那个临时对象了,既然你可以移动它的内容,为什么还要复制它呢?

除此之外,可能值得一提的是,大多数 C++ 编译器,甚至是 C++11 之前的编译器,都实现了(命名的)返回值优化,这无论如何都会省略副本,不会产生任何开销。因此,您可能希望在优化之前实际测量(可能)获得的性能损失。

我认为只有在需要引用语义时才应该通过引用传递或返回共享指针。这似乎不是你的情况。

于 2013-02-21T14:10:05.727 回答
1

信不信由你,我将建议不要采用任何一种方法,而是采用明显的实现并按值返回!编译器通常能够优化掉将被诱导的名义副本,将其完全删除。通过以最明显的方式编写代码,您可以让未来的维护者非常清楚其意图是什么。

但是假设您尝试按值返回并且您的程序运行速度太慢让我们进一步假设您的分析器显示按值返回实际上是您的瓶颈。在这种情况下,我将在堆上分配容器并auto_ptr在 C++03 或unique_ptrC++11 中以 a 的形式返回,以清楚地表明所有权正在转移,并且生成器不会shared_ptr为自己保留一份副本以后的目的。

最后,http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/上的系列文章为几乎完全相同的问题提供了很好的视角。

于 2013-02-21T18:48:32.593 回答
1

最佳实践可能会让您感到惊讶。我建议在 C++03 和 C++11 中按值返回。

  • 在 C++03 中,如果您创建一个std::vector本地 togenerate并返回它,则该副本可能会被编译器忽略(几乎肯定会)。参见 C++03 §12.8/15:

    在具有类返回类型的函数中的return语句中,当表达式是与函数返回类型具有相同cv非限定类型的非易失性自动对象的名称时,可以通过构造自动对象来省略复制操作直接进入函数的返回值

  • 在 C++11 中,如果您创建一个std::vector本地 togenerate并返回它,则该副本将首先被视为一个移动(这已经非常快),然后可能会被忽略(几乎肯定会)。参见 C++11 §12.8/31:

    在具有类返回类型的函数的 return 语句中,当表达式是具有与函数返回类型相同的 cv 非限定类型的非易失性自动对象(函数或 catch 子句参数除外)的名称时,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作

    和§12.8/32:

    当满足或将满足复制操作的省略标准时,除了源对象是函数参数的事实,并且要复制的对象由左值指定时,选择复制的构造函数的重载决策是首先执行好像对象是由右值指定的。

所以按价值返回!

于 2013-02-21T14:11:15.740 回答
1

有一种替代方法。如果您可以使您的函数模板化,则使它们将输出迭代器(其类型是模板参数)作为参数:

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

这样做的好处是调用者可以决定他想在哪种集合中存储结果(输出迭代器可以例如直接写入文件,或将结果存储在 a 中std::vector,或过滤它等)。

于 2013-02-21T14:10:45.923 回答