Reputation: 4677
I am in the process of converting some legacy code to take advantage of smart pointers in C++. However, I ran into a runtime issue that I'm struggling to figure out. I have code such as the following:
struct foo {
int field;
};
class SomeClass {
private:
std::unique_ptr<foo> m_Foo;
int m_Field;
public:
SomeClass(std::unique_ptr<int> ctorArg)
: m_Field(ctorArg->field), m_Foo(std::move(ctorArg)) {
}
};
std::unique_ptr<foo> fooPtr{ new foo() };
auto const classInstance{ std::make_unique<SomeClass>(std::move(fooPtr)) };
If I put a breakpoint prior to calling the SomeClass
constructor, I can verify that fooPtr
is not null. However, once I am within the SomeClass
constructor, the application is crashing when trying to initialize m_Field
, since ctorArg
is null (empty). Is this expected? If I change the constructor signature to take std::unique_ptr<int> &&
, I see the same issue. Can someone please explain what the issue is with this code, and how to go about fixing it?
Upvotes: 2
Views: 261
Reputation: 85531
The order of member-initializer-list is irrelevant, the members will be initialized in the order of their declaration.
So, first m_Foo(std::move(ctorArg))
will zero out ctorArg
then m_Field(ctorArg->field)
will attempt to derefence an empty ctorArg
.
Change your code to:
class SomeClass {
private:
std::unique_ptr<foo> m_Foo;
int m_Field;
public:
SomeClass(std::unique_ptr<int> ctorArg)
: m_Foo(std::move(ctorArg)), m_Field(m_Foo->field) {
}
};
That is, always mention initializers in the order the fields are declared, and don't use input arguments which have been moved-out from.
Upvotes: 2