将传递引用转换为传递返回

Transforming pass-by reference into pass-by return

本文关键字:返回 引用 转换      更新时间:2023-10-16

我有以下函数:

void read_int(std::vector<int> &myVector)

这使我可以通过它引用来填充myVector。它是这样用的:

std::vector<int> myVector;
read_int(myVector);

我想重构一些代码(保留原始函数(以最终拥有以下内容:

auto myVector = read_int();   // auto is std::vector<int> 

实现此目的的最佳中间函数是什么?


在我看来,以下直接的答案是次优的:

std::vector<int> read_int() {
    std::vector<int> myVector_temp;
    read_int(myVector_temp);
    return myVector_temp;
}

显而易见的答案是正确的,而且基本上是最优的。

void do_stufF(std::vector<int>& on_this);    // (1)
std::vector<int> do_stuff_better() {         // (2)
    std::vector<int> myVector_temp;          // (3)
    do_stuff(myVector_temp);                 // (4)
    return myVector_temp;                    // (5)
}

(3( 处,我们在自动存储(在堆栈上(创建一个命名的返回值。

(5( 处,我们只从函数返回命名的返回值,并且除了函数中其他任何地方的命名返回值之外,我们永远不会返回任何其他内容。

由于 (3( 和 (5(,编译器被允许(并且很可能会(消除myVector_temp对象的存在。 它将直接构造函数的返回值,并调用它myVector_temp。 它仍然需要有一个现有的移动或复制构造函数,但它不会调用它。

另一方面,当调用do_stuff_better时,一些编译器也可以在调用时省略赋值:

std::vector<int> bob = do_stuff_better();     // (6)

编译器被允许有效地传递一个"指向 bob 的指针",并告诉do_stuff_better()bob 的位置构造它的返回值,也省略了这个副本构造(嗯,它可以安排调用的发生方式,使得do_stuff_better()被要求构造其返回值的位置与bob的位置相同(。

而在 C++11 中,即使两个省略号的要求都不满足,或者编译器选择不使用它们,在这两种情况下都必须执行move而不是copy

在第 (5( 行,我们以简单明了的 return 语句返回一个本地声明的自动存储持续时间变量。 如果不省略,这使得返回成为隐式move

在第 (6( 行,该函数返回一个未命名的对象,这是一个右值。 当bob从它构造时,它move构造。

move std::vector包括复制 ~3 个指针的值,然后将源归零,而不管vector有多大。 无需复制或移动任何元素。

上述两个省略,我们去掉do_stuff_better()内的命名局部变量,去掉do_stuff_better()return值,直接构造bob,都有些脆弱。 学习允许编译器执行这些省略的规则,以及编译器实际执行省略的情况,是值得的。

作为它如何脆弱的示例,如果您有一个分支,在检查错误状态后在do_stuff_better()中执行了return std::vector<int>(),则函数中的省略可能会被阻止。

即使 elision 被阻止或编译器没有针对某个案例实现它,容器move的事实也意味着运行时成本将降至最低。

我认为

,您必须阅读更多有关移动语义的信息(链接到Google查询,有很多关于此的论文 - 只需选择一个(。

简而言之,C++所有 STL 容器都是以这样的方式编写的,从函数返回它们将导致它们的内容从返回值(所谓的右侧引用(移动到您将其分配给的变量。实际上,您只会复制 std::vector 的几个字段,而不是其数据。这比复制其内容要快得多。