为什么不调用move构造函数

时间:2018-08-24 18:50:05

标签: c++

因此,我不熟悉语义,并且正在测试以下代码。我的理解是rvalue将调用move构造函数,并且我期望A(“ 123”)将导致move构造函数被调用。但是当我运行此代码时,将调用复制构造函数。

#include <string>
#include <iostream>
#include <utility>

class  A
{
    std::string s;
    public:

    A(const std::string& in) : s(in) { std::cout << "ctor!\n";}
    A(const A& o) : s(o.s) { std::cout << "move failed!\n"; }
    A(A&& o) noexcept : s(std::move(o.s)) { }
};

class  B
{
    A d_a;
    public:
    B(const A& a) :d_a(a)
{}
};

int main()
{
    std::cout << "Trying to move A\n";
    B b(A("123")); // move-constructs from rvalue temporary

}

2 个答案:

答案 0 :(得分:5)

问题是B的构造函数:

B(const A& a) :d_a(a) {}

函数参数const A&是const限定的左值引用,您不能将其强制转换为右值。您需要将构造函数更改为(或添加第二个)

B(A&& a) : d_a(std::move(a)) {}

作为旁注,如果您将示例定义为以下类型,则可以免费获得示例中类型的正确移动语义

struct  A {
    std::string s;
};

struct B {
     A d_a;
};

带有客户代码

B b{A{"123"}};

我知道您不希望为了研究move-construction而不要依赖编译器生成的特殊成员函数,我只是不想忽略此快捷方式,因为这是我们应该努力的设置:类的复制和移动语义将由其数据成员自动组装。

答案 1 :(得分:2)

const左值引用绑定到任何东西。您的B具有一个通过const左值引用接受A的构造函数。当您向此构造函数传递临时A时,最终会导致某些参数设置看起来像

const A& tmp = temporary_A;

,从那时起,您的A将被视为const左值引用,该调用将调用copy构造函数,因为它与签名匹配。您需要在B中定义一个构造函数,该构造函数以一个右值引用来接受A

B(A&& a)

目前情况如此,您将看到类似以下内容的打印输出:

  

尝试移动A

     

ctor!

     

移动失败!

构造临时文件时会打印

ctor!,因为您的参数被当作const左值引用,d_a(a)会调用复制构造函数。下面,修改后的代码避免了复制。

#include <string>
#include <iostream>
#include <utility>

class  A
{
    std::string s;
    public:

    A(const std::string& in) : s(in) { std::cout << "ctor!\n";}
    A(const A& o) : s(o.s) { std::cout << "move failed!\n"; }
    A(A&& o) noexcept : s(std::move(o.s)) { }
};

class  B
{
    A d_a;
    public:
    B(A&& a) :d_a(std::move(a))
    {}
};

int main()
{
    std::cout << "Trying to move A\n";
    B b(A("123")); // move-constructs from rvalue temporary
}