stl 数据结构的输出参数与堆栈上的返回值的效率

问题描述 投票:0回答:4

假设我有这些功能

std::Vector<Point> calculate() {
   std::Vector<Point> points; //do stuff with points
   return points;
}

void calculate(std::Vector<Point>& points) {
   //do stuff with points
}

所以我的问题特定于在堆栈上初始化的对象,并且是 stl 对象。性能有什么区别吗?流行的方法是什么

问候

c++ performance stl c++11
4个回答
16
投票

以值作为参考参数,具有以下性质:

  1. 不得进行复制、移动或任何其他操作。
  2. 返回值在用户端不能立即丢弃。他们不能只是将临时参数推入函数的引用参数或其他东西。他们必须声明一个变量,因此他们必须给它一个在当前作用域内存在的名称。
  3. API 建议 该值是输入/输出参数。也就是说,传递了一个将被读取和写入的值。如果情况并非如此,那么使用它就代表了次优的 API 设计元素。

返回值具有以下属性:

  1. 如果复制省略不可用(由于函数实现的性质、较差的编译器或返回值未初始化新值),则返回值将是 moved。没有复制。移动不是免费的,但一般来说它并不比几个指针副本贵多少。不会分配或释放新的对象或内存。
  2. API 强制值的输出性质。用户无法使用它传入的输出,因为它没有传入任何内容。同样,该函数也无法读取任何值,因为它没有传入任何内容。它是一个输出值、期间;该函数生成它并返回它。
  3. 用户可以自行决定立即丢弃返回值。显然,如果用户经常这样做,则表明出现了问题,但由用户决定是否要保留输出值。 C++17
    [[nodiscard]]
    属性可用于丢弃值绝对不正确的情况。

11
投票

由于复制省略,性能很可能是相同的。

两种方式表达的内容不同

std::vector<Point> calculate()

返回一个向量(可能基于某些参数)。

void calculate(std::vector<Point>& points)

修改现有向量(同样,可能基于参数)。


1
投票

我的拙见:大的返回值会产生比解决更多的问题......它们看起来更简单,它们在调用段中更简洁,但会避免右值引用。

如果你看看旧的、但专为效率而设计的软件包,如 BLAS 和 LAPACK,大对象是通过引用传递的,返回值只是错误标志或双精度(例如 BLAS 中的 ddot)。

缺点是表现力会降低,但成本很小,因为内存分配等资源会向上游移动。

C++ 在语法和资源管理方面可以得到显着简化。传递非常量引用在“美观”和代码长度方面会产生成本,但它是有效的。

例如,在 BLAS 中:

如果是小件物品,可以退货,例如: DDOT

double cblas_ddot(const int N, const double *X, const int incX,
                  const double *Y, const int incY);

如果它很重要,比如矩阵,例如DGEMM,

void cblas_dgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,const enum CBLAS_TRANSPOSE TransB, const int M, const int N,const int K, const double alpha, const double *A,                    const int lda, const double *B, const int ldb,const double beta, double *C, const int ldc);

  

这在科学计算中是众所周知的——传递大对象的指针或非常量引用。


0
投票

在 Google 编码约定中,他们建议使用返回值而不是输出参数。这是因为返回值提高了可读性并且通常提供相同或更好的性能。

为了可读性,我认为这是显而易见的。对于输出参数,您必须查看所有参数并确定哪些参数用于输出参数。

对于性能,Scot Meyer 在他的书中说“尽可能使用

const
”。我们不能使用
const
作为输出参数。此外,现代编译器使用返回值优化(RVO)或命名返回值优化(NRVO)来优化复制操作。

阅读以下内容了解更多详情:[1][2]

© www.soinside.com 2019 - 2024. All rights reserved.