【问题标题】:Passing vector with std::move function signature使用 std::move 函数签名传递向量
【发布时间】:2020-01-20 20:52:55
【问题描述】:

考虑下面的代码

void foo(std::vector<int> v)
{
   //do something here
}
//calling the function
vector<int> v1 = {1,2,3};
foo(std::move(v1));

我的问题是,函数 foo 是否应该具有签名 void foo(std::vector&lt;int&gt;&amp;&amp; v) 才能获取向量的 r 值引用?

【问题讨论】:

    标签: c++ move-semantics rvalue-reference


    【解决方案1】:

    我的问题是,函数 foo 不应该具有签名 void foo(std::vector&lt;int&gt;&amp;&amp; v) 以便能够获取向量的 r 值引用吗?

    如果这就是你想要的,那么是的,但这并不意味着你所拥有的不正确。当您将某些内容传递给函数时,复制会从源中初始化参数。这意味着如果你这样做

    vector<int> v1 = {1,2,3};
    foo(v1);
    

    然后foo 获得v1 的副本。与

    vector<int> v1 = {1,2,3};
    foo(std::move(v1));
    

    我们从std::move(v1) 复制初始化v,由于std::move(v1) 是一个右值引用,因此为v 选择了移动构造函数,并且v1 被移动到函数中。

    因此,通过取值,您可以让调用者选择给它一个临时值,给它一个右值引用,这既可以将对象移动到函数中,也可以让一个副本发生。如果你有void foo(std::vector&lt;int&gt;&amp;&amp; v) 那么你只能传递一个临时的或std::move() 一个左值。如果调用者自己制作一个副本,然后将该副本移动到函数中,就无法允许调用者制作副本。

    【讨论】:

    • 感谢您的回答,但我仍然不明白函数调用的顺序(就何时准确调用移动构造函数而言)。 “从 std::move(v1) 和 ... 复制初始化 v”究竟是什么意思
    • 我的意思是,给定向量类也有一个复制构造函数(带有 const vector&)作为签名,然后当我调用 foo(move(v1)),然后 move(v1)将是一个vector&&,但即使是右值引用也是一个左值引用,那为什么不调用复制构造函数呢?
    • @Kraken std::move 只是一个将您提供的内容转换为右值引用的函数。然后将该右值引用传递给foo,编译器将使用它来初始化v。由于它是一个右值引用,因此该类型匹配向量移动构造函数,因此将调用移动构造函数而不是复制构造函数。有意义吗?
    • 是的,有道理。我认为归结起来,编译器需要初始化 v,为此,它需要根据我传递的内容使用复制或移动构造函数。我很可能会问一个问题,即当我传递 foo(v1) 时如何调用复制构造函数,答案是编译器根据我提供的输入调用了复制构造函数。 :) 谢谢。
    【解决方案2】:

    函数 foo 不应该具有签名 void foo(std::vector&lt;int&gt;&amp;&amp; v) 以便能够获取向量的 r 值引用吗?

    具有这样签名的是std::vector&lt;int&gt;的移动构造函数。

    函数foo()std::vector&lt;int&gt; 对象作为参数按值

    void foo(std::vector<int> v) {
       // ...
    }
    

    parameter 对象v 必须以某种方式构造。传递给foo()参数用于构造这个参数对象。

    通过在表达式foo(std::move(v1)) 中调用foo()foo() 中的参数对象v移动构造。这与在表达式foo(v1) 中调用foo() 形成对比,后者导致参数对象v复制构造

    因此,参数对象的移动构造函数(即vstd::vector&lt;int&gt; 对象)采用std::vector&lt;int&gt;&amp;&amp;

    【讨论】:

      【解决方案3】:
      void foo(std::vector<int> v)
      

      表示函数将std::vector&lt;int&gt;object作为参数。调用者可以根据需要构造这个参数对象,但是它由foo拥有和使用。

      在调用者中,你有:

      foo(/**compiler constructs the v parameter from whatever is here**/);
      

      因此:

      vector<int> v1 = {1,2,3};
      foo(v1); // This constructs the parameter `v` as a copy of `v1`.
      

      对比

      vector<int> v1 = {1,2,3};
      foo(std::move(v1)); // This constructs the parameter `v` by moving from the value of `v1`.
      

      两者都有效,但它们做的事情略有不同。


      void foo(std::vector<int>&& v)
      

      表示该函数将reference作为参数,它引用了其他地方的std::vector&lt;int&gt;,也意味着调用者在此函数完成后不应使用该参数,因为调用者打算以某种方式使其无效。

      该语言有助于防止此处的错误

      • 强制你传递一个临时值(这样调用者就不会在foo 完成后意外尝试使用引用的vector 的值),
      • 或强迫你调用std::move,以保证编译器在foo完成后不会尝试使用引用的vector的值)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-14
        • 2019-01-13
        相关资源
        最近更新 更多