I have two similar pieces of code. The first version unexpectedly calls the default constructor while the second doesn't. They both call the move operator / move constructor, respectively, as expected.
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;
};
My test usage is:
MyWrapper* wrapper = new MyWrapper(MyResource(1));
delete wrapper;
With Version 1, I get:
Int Ctor
Default Ctor
Move Op
Dtor
Dtor
While Version 2 outputs:
Int Ctor
Move Ctor
Dtor
Dtor
What's the reason behind this difference?
Why does version 1 call the default constructor?
CodePudding user response:
Members are initialized before the construct body runs. A much simpler example to see the same:
#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);
}
default ctr
default ctr
assignment
---------
ctr
You cannot initialize a member in the body of the constructor! If you do not provide an initializer, either in the member initializer list or as an in class initializer, then f
is default constructed. In the constructor body you can only assign to an already initialized member.