【问题标题】:Why move ctor is not called in this case?为什么在这种情况下不调用 move ctor?
【发布时间】:2021-08-09 13:35:57
【问题描述】:
#include <iostream>

using namespace std;

class Test {
public:
    
    Test(string value){
        cout<<"Ctor "<<value<<endl;
        _val=value;
    }
    Test( Test&& mv): _val(mv._val)
    {
        mv._val=string();
        cout<<"Mv constructor"<<endl;  
    }
    
    string& get()
    {
      return this->_val;
    }
private:
    string _val;
};

void print(Test&& t)
{
    cout<<"Stampa val is "<<t.get()<<endl;
}

int main()
{
    Test a{"ciao"};
    print(move(a));
    cout<<"Val of a is "<<a.get()<<endl;
    print(Test("test"));
    

    return 0;
}

这个输出是(将行号添加到标准输出):

曹操
Stampa val is ciao
val 是 ciao
托儿测试
Stampa val 是测试

为什么在 main 的第 2 行没有调用 mv 语义?我可能会在第四行理解有一个优化,所以只调用了构造函数,但是我无法解释第一步。有什么想法吗?

【问题讨论】:

  • std::move() 只转换为右值,实际上并没有做任何事情。
  • 因为调用print时不需要复制/移动Test实例。 print 采用 Rvalue 引用,并且您通过 move 传递 Rvalue 引用转换 a。顺便说一句,修复你的移动 ctor:_val(std::move(mv._val)).
  • Move构造函数是一个构造函数,也就是说它构造了一个新的对象。在print(move(a)); 语句中,根本没有涉及新对象,因此没有调用构造函数。

标签: c++ move-semantics stdmove


【解决方案1】:

std::move 只是将参数转换为右值(可能稍后移动),它本身不执行移动操作。转换后的右值绑定到引用参数t,因此在这种情况下不会调用移动构造函数。

std::move 用于表示一个对象 t 可能被“移出”,即允许将资源从 t 有效地转移到另一个对象。

特别是,std::move 产生一个 xvalue 表达式来标识它的参数 t。它完全等同于将 static_cast 转换为右值引用类型。

通常调用移动构造函数来初始化对象,它不会在引用绑定中调用(对于左值引用也是如此)。如果您更改要按值传递的参数,将使用移动构造函数(初始化参数)。例如

void print(Test t)
{
    cout<<"Stampa val is "<<t.get()<<endl;
}

LIVE


顺便说一句:即使更改为按值传递 print(Test("test")); 后,也不会因为 copy elision 而调用移动构造函数。

BTW2:在移动构造函数中,最好根据std::string 提供的移动操作移动初始化数据成员val。例如

Test( Test&& mv): _val(std::move(mv._val))
{
    cout<<"Mv constructor"<<endl;  
}

【讨论】:

  • 好的,谢谢!但是在转换之后为什么不应该调用它呢?它是一种优化吗,不应该调用移动语义吗?还是我们使用双 && 表示我们只需要一个右值因此不调用 mv 构造函数的情况?
  • @HDenied 答案已修改。
  • @HDenied T&amp;&amp; 是对T 的引用。它与 T&amp; 的区别仅在于可以绑定到引用的内容
  • @songyuanyao 谢谢,特别是与初始化和引用绑定相关的部分让我理解了这个概念
【解决方案2】:

您的代码中只构造了两个 Test 对象,没有更多。

int main()
{
    Test a{"ciao"};                          // here 
    print(move(a));
    cout<<"Val of a is "<<a.get()<<endl;
    print(Test("test"));                     // and here
}

第二个也通过Test(string)构造函数构造对象。 std::move 不构造对象,它只是对右值引用的强制转换。将该右值引用传递给print 也不需要构造另一个Test。如果要调用构造函数,则必须实际构造一个实例,例如:

 auto t  = Test( std::move(a) );     // calls Test(Test&&)
 auto t2 = Test( Test("move me") );  // calls Test(string) then Test(Test&&)

【讨论】:

    猜你喜欢
    • 2022-11-02
    • 2013-08-18
    • 2019-11-09
    • 1970-01-01
    • 2017-01-07
    • 2020-04-21
    • 2014-07-13
    • 2016-06-04
    • 1970-01-01
    相关资源
    最近更新 更多