【问题标题】:Transforming pass-by reference into pass-by return将传递引用转换为传递返回
【发布时间】:2013-08-01 05:05: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;
}

【问题讨论】:

  • 为什么你认为这是次优的?
  • 新函数不会创建一个中间vector&lt;int&gt;,然后将其复制到声明中吗?
  • 你所拥有的看起来比你原来的版本要好得多。如果您担心性能,请阅读return value optimization
  • 这很好,尽管最好不包装函数并实际重构。 std::vector 将在可以删除时被删除,在不能时移动,因此按值返回向量几乎是正确的方法。
  • @juanchopanza:谢谢,不知道返回值优化!

标签: c++ function c++11 refactoring pass-by-reference


【解决方案1】:

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

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-constructs。

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

上述两种省略(我们删除do_stuff_better() 中的命名局部变量,以及删除do_stuff_better()return 值并直接构造bob)都有些脆弱。了解允许编译器执行这些省略的规则,以及编译器实际执行省略的情况是值得的。

作为一个脆弱的例子,如果你有一个分支,你在检查错误状态后在你的do_stuff_better() 中执行了return std::vector&lt;int&gt;(),那么函数内省略可能会被阻止。

即使省略被阻止或者您的编译器没有针对某个案例实现它,容器为move'd 的事实意味着运行时间成本将是最小的。

【讨论】:

    【解决方案2】:

    我认为,您必须阅读更多关于 move semantics 的信息(链接到 Google 查询,有很多关于此的论文 - 只需选择一个)。

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

    【讨论】:

    • 只有在没有省略副本的情况下才会发生这种情况。复制省略也适用于 C++03 :)
    • @juanchopanza 当然可以,但无论如何这个问题都被标记为 C++11。尽管如此,这确实是一个值得注意的事实。
    猜你喜欢
    • 2016-11-15
    • 2013-09-20
    • 1970-01-01
    • 2019-09-10
    • 2011-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多