Jeff G
Jeff G

Reputation: 4677

C++ Smart Pointer Lost in std::make_unique

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

Answers (1)

rustyx
rustyx

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

Related Questions