【问题标题】:c++ will compiler optimize argument by value to argument by rvalue reference?c++ 编译器会按值优化参数到右值引用的参数吗?
【发布时间】:2021-12-23 20:04:20
【问题描述】:

我想记得有人告诉我这两种方法

foo(std::vector<int>&& v) {
  member = std::move(v);
}

bar(std::vector<int> v) {
  member = std::move(v);
}

如果调用站点发生移动,两者都不会调用副本

foo(std::move(v1));
bar(std::move(v2));

因为编译器将优化调用并将bar 的情况视为通过右值引用获取参数,因此应该支持bar 语法,因为它不那么复杂并且也可以用左值调用(在这种情况下,当然会制作副本)。

这是真的吗?只要有人用右值调用它,就可以依赖barcase 不创建副本吗?

【问题讨论】:

  • 两者都可以。人们经常为第二种情况争论不休,但这可能会在不需要时涉及额外的动作
  • 这个问题的一个相关说明是std::vector 有一个移动构造函数,它是高效的。其他类型可能不会。
  • @DrewDormann 哦,原来如此。 value 参数是通过其 move 构造函数构造的,这就是为什么只要使用右值调用函数就不会创建副本?
  • @matthias_buehlmann 是的!这使得bar 成为左值和右值的不错选择。您的 foo 仅接受 r 值。
  • 如果打算始终移出参数,则第一个版本更安全,因为它不会绑定到可变的左值引用并进行复制。

标签: c++ move-semantics rvalue


【解决方案1】:

只要使用右值调用它,就可以依赖bar 的情况不创建副本吗?

不,不一定。 将进行复制的示例:

const std::vector<int> vec;

bar( std::move(vec) );

这里std::move 会将vec(一个左值)转换为一个xvalue(即rvalue)。但它不会被移动,因为vec常量,不能修改。因此std::vectormove构造函数不会被调用。

现在我在godbolt 中对您的代码进行了一些修改,然后得到了您预期的结果。

这段代码:

#include <iostream>
#include <vector>
#include <utility>

void foo( std::vector<int>&& v )
{
    auto member = std::move(v);
}

void bar( std::vector<int> v )
{
    auto member = std::move(v);
}


int main( )
{
    std::vector<int> v1(3, 1000);
    std::vector<int> v2(3, 2000);

    //foo( std::move(v1) ); // uncomment this
    //bar( std::move(v2) ); // uncomment this
    bar( v2 );              // and comment this out

    for ( const auto& num : v1 )
    {
        std::cout << num << ' ';
    }

    std::cout << '\n';

    for ( const auto& num : v2 )
    {
        std::cout << num << ' ';
    }

    std::cout << '\n';
}

首先,您将看到如下输出:

1000 1000 1000 
2000 2000 2000 

但是然后做我上面提到的事情,你会看到输出会消失。这是因为在移动这两个向量之后,由于失去了缓冲区的所有权,它们将变为空。所以 for 循环不会打印任何内容。这确实证明了bar( std::move(v2) ); 发生了变化。而bar( v2 ); 是深拷贝。

【讨论】:

  • 虽然在这种情况下,问题出在调用者身上,因为const 对象上的std::move 无论如何都没有多大意义。
  • LEA 意味着它加载的是地址而不是值(MOV),不是吗?
  • @matthias_buehlmann 这是 GCC v11.2 在启用 -O3 的情况下生成的。我认为这是对复制构造函数的调用。否则我们会看到std::vector&lt;int, std::allocator&lt;int&gt; &gt;&amp; 用于左值引用或std::vector&lt;int, std::allocator&lt;int&gt; &gt;&amp;&amp; 用于右值引用。
  • bar( std::move(v2) ) 进行 2 次移动(无副本)。参数是从参数移动构造的
  • @digito_evo 通过了解语言标准,您可以阅读标准和/或阅读有关该主题的其他高度评价的答案
猜你喜欢
  • 1970-01-01
  • 2012-12-26
  • 2010-09-21
  • 1970-01-01
  • 2015-08-01
  • 2010-09-21
  • 2012-02-19
  • 1970-01-01
  • 2021-12-22
相关资源
最近更新 更多