返回容器

时间:2017-11-19 14:00:39

标签: c++ output-parameter

返回容器时,我总是要确定是否应该使用返回值或使用输出参数。如果性能很重要,我选择了第二个选项,否则我总是选择第一个选项,因为它更直观。

坦率地说,我个人一直强烈反对输出参数,可能是因为我的数学背景,但是当我没有其他选择时,可以使用它们。

然而,在泛型编程方面,事情已经彻底改变了。我遇到的情况是函数可能不知道它返回的对象是一个巨大的容器还是一个简单的值。

如果可以避免的话,一直使用输出参数可能是我想知道的解决方案。如果我不得不这样做很尴尬。

int a;
f(a, other_arguments);

相比
auto a = f(other_arguments);

此外,有时返回类型f()没有默认构造函数。如果使用输出参数,则没有优雅的方法来处理这种情况。

我想知道是否可以返回一个"修饰符对象",一个带有输出参数的仿函数来适当地修改它们。 (也许这是一种懒惰的评估?)好吧,返回这样的对象不是问题,但问题是我不能插入一个适当的重载赋值运算符(或构造函数)来获取这样的对象和触发器它可以完成它的工作,当返回类型属于我无法触及的库时,例如std::vector。当然,转换运算符没有帮助,因为它们无法访问为目标对象准备的现有资源。

有些人可能会问为什么不使用assign();定义一个"生成器对象"其中有begin()& end(),并将这些迭代器传递给std::vector::assign。这不是一个解决方案。出于第一个原因,"生成器对象"没有对目标对象的完全访问权限,这可能会限制可以执行的操作。对于第二个也是更重要的原因,我的函数f()的调用网站也可能是一个不知道f()的确切返回类型的模板,因此它无法确定哪个赋值运算符或者应该使用assign()成员函数。

我认为"修饰符对象"修改容器的方法应该在过去已经讨论过,因为它根本不是一个新想法。

总结一下,

  1. 是否可以使用返回值来模拟使用输出参数时会发生什么,特别是当输出是容器时?
  2. 如果没有,是否将这些支持添加到之前讨论过的标准中?如果是的话,问题是什么?这是一个糟糕的主意吗?
  3. 修改

    我上面提到的代码示例具有误导性。函数f()可用于初始化局部变量,但也可用于修改其他地方定义的现有变量。对于第一种情况,正如Rakete1111所提到的那样,随着复制省略的发挥,按值返回没有问题。但对于第二种情况,可能会有不必要的资源释放/获取。

2 个答案:

答案 0 :(得分:1)

我认为你的“修饰对象”并没有被提出(AFAIK)。它永远不会进入标准。为什么?因为我们已经有办法摆脱昂贵的副本,那就是按值返回(加上编译器优化)。

在C ++ 17之前,允许编译器基本上做同样的事情。此优化称为(N)RVO,它在从函数返回(命名)临时值时优化副本。

auto a = f(other_arguments);

不会返回临时,然后将其复制到a。编译器将完全优化副本,不需要它。理论上,你不能假设你的编译器支持这个,但三个主要的(clang,gcc,MSVC)所以不用担心 - 我不知道ICC和其他人,所以我不能说。 / p>

因此,由于没有涉及复制(或移动),使用返回值而不是输出参数没有性能损失(最可能的情况是,如果由于某种原因你的编译器不支持它,你会得到一个移动大部分时间)。如果可能的话,你应该总是使用返回参数,如果你测量的话,你只能使用输出参数或其他技术,否则你会获得更好的性能。

答案 1 :(得分:-2)

(根据评论编辑)

你是对的,你应该尽可能避免使用输出参数,因为使用它们的代码更难以阅读和调试。

从C ++ 11开始,我们有一个名为move constructors (see reference)的功能。您可以在所有原始和STL容器类型上使用1 2 4 8 16 32 64 128 256 512 。效率很高(问题:Efficiency difference between copy and move constructor),因为您实际上并未复制变量的值。只交换指针。对于您自己的复杂类型,您可以编写自己的移动构造函数。 另一方面,您唯一能做的就是返回对临时的引用,这是一种未定义的行为,例如:

std::move

它的输出可以:     7

你可以避免使用全局或静态变量的临时变量问题,但它也很糟糕。 @Rakete是对的,没有好办法实现它。