为什么通过引用返回向量比通过移动返回快得多?

Why returning a vector by reference is much faster than returning by move?

我有两种类似的方法,这两种方法都通过引用获取 std::vector 然后其中一种尝试通过引用 return 它(因为生命周期已满足)和另一种 returns 移动。在这两种情况下,我都不需要之后传递的向量,它只是为了满足语法要求。

两个函数看起来像 -

std::vector<unique_ptr<Item>> func_returns_move(std::vector<unique_ptr<Item>> &items) {
    items.erase(std::remove_if(items.begin(), items.end(),
    [&](const std::unique_ptr<Item> &item) {
        return item->age > 20;
    }), items.end());
    return move(items);
}


std::vector<unique_ptr<Item>>& func_returns_ref(std::vector<unique_ptr<Item>> &items) {
    items.erase(std::remove_if(items.begin(), items.end(),
    [&](const std::unique_ptr<Item> &item) {
        return item->age > 20;
    }), items.end());
    return items;
}

根据我的假设,这里应该没有太大区别,因为唯一不同的是最后一行 - 一条 returns 由 ref 和另一条直接移动。起初我认为差异可能是由于 move 创建了一个临时对象,默认情况下可能是 malloc 或其他东西,这将是像 result = func_returns_move(...) 这样的函数的结果,但它应该是恒定时间,但这里不是这种情况。花费的时间与向量中的元素数量成正比。所以我不明白是什么导致了 return by std::move 案例的更多执行。


时间安排如下-

FUNC_RETURNS_MOVE: 50
FUNC_RETURNS_REF: 5

这里是测试程序 - https://godbolt.org/z/5Y8Mcw

在第二种情况下,vector 在范围退出时被销毁(唯一的 vector 对象是 items2)。这在您的计时区之外。

然而,在第一种情况下,返回的移动构造的临时向量在完整表达式的末尾被销毁,即 定时部分中。 items1 在范围退出时仍然被销毁,但此时它是空的。

两种情况的实际时间成本基本相同。但是在第一种情况下,所有元素的销毁都在定时区域内,而在第二种情况下则在外部。

如果我们将返回的vector对象的销毁保持在时序之外,我们会得到两者一致的结果(并且可以看到移动确实是"free"):https://godbolt.org/z/z-wKVG

(由于技术许可原因,我不会在此处复制相关的 godbolt 代码 - 它需要在问题中。)