使用移动语义时意外的默认构造函数调用

Unexpected default constructor call when using move semantics

我有两段相似的代码。第一个版本意外地调用了默认构造函数,而第二个版本则没有。他们都按预期分别调用移动运算符/移动构造函数。

class MyResource
{
public:
    MyResource() : m_data(0) { std::cout << "Default Ctor" << std::endl; }
    MyResource(int data) : m_data(data) { std::cout << "Int Ctor" << std::endl; }

    MyResource(MyResource const& other) = delete;
    MyResource& operator=(MyResource const& other) = delete;

    MyResource(MyResource&& other) noexcept : m_data(other.m_data) { std::cout << "Move Ctor" << std::endl; }
    MyResource& operator=(MyResource&& other) noexcept { std::cout << "Move Op" << std::endl; m_data = other.m_data; return *this; }

    ~MyResource() { std::cout << "Dtor" << std::endl; }

private:
    int m_data = 0;
};

class MyWrapper
{
public:
    MyWrapper(MyResource&& resource)
        // : m_resource(std::move(resource)) // Version 2
    {
        // m_resource = std::move(resource); // Version 1
    }

private:
    MyResource m_resource;
};

我的测试用法是:

MyWrapper* wrapper = new MyWrapper(MyResource(1));
delete wrapper;

对于版本 1,我得到:

Int Ctor
Default Ctor
Move Op
Dtor
Dtor

虽然版本 2 输出:

Int Ctor
Move Ctor
Dtor
Dtor

造成这种差异的原因是什么?
为什么版本 1 会调用默认构造函数?

成员在构造体运行之前被初始化。一个更简单的例子来查看相同的内容:

#include <iostream>

struct foo {
    foo(int) { std::cout << "ctr\n";}
    foo() { std::cout << "default ctr\n";}
    void operator=(const foo&) { std::cout << "assignment\n"; }
};

struct bar {
    foo f;
    bar(int) : f(1) {}
    bar() {
        f = foo();
    }
};

int main() {
    bar b;
    std::cout << "---------\n";
    bar c(1);
}

Output:

default ctr
default ctr
assignment
---------
ctr

您不能在构造函数的主体中初始化成员!如果您不提供初始化器,无论是在成员初始化器列表中还是作为 in class 初始化器,则 f 是默认构造的。在构造函数体中,您只能分配给已经初始化的成员。