Perhaps I was trying to be too generic. (Original question below) Concretely, I have some dependency Dep of a class Foo. I also have a class MockDep and am defining a class TestFoo. Here is its constructor I tried to write:
TestFoo(unique_ptr<MockDep> dep) : Foo(std::move(dep)), mock_dep_(dep.get()) {}
And Foo's constructor looks like:
Foo(unique_ptr<Dep> dep) : dep_(dep) {}
mock_dep_ is delcared in TestFoo as MockDep* mock_dep_, and dep_ is declared in Foo as unique_ptr<Dep> dep_. How can I get mock_dep_ to contain dep_'s address? (as the above doesn't work since std::move(dep) nulls out dep.)
Original post:
I have an object of type Foo that I'm to pass to a different object of type OtherObject which claims ownership of it, but as a pointer to its base class. However, I want to grab a pointer to the child object that I can use to reference it. I wrote something like:
Foo(std::unique_ptr<Child> thing) :
OtherObject(std::move(thing)), child_(thing.get()) {}
OtherObject(std::unique_ptr<Base> thing, ...) { ... }
However, this doesn't seem to work, as the std::move(thing) seems to null out the pointer that returns from thing.get() later.
I can change Foo's parameter to be of type Child* instead of unique_ptr<Child>, but I'd prefer to be able to do the latter as it explicitly documents the ownership semantics.
What's the most appropriate (or failing that, unobtrusive) way of resolving this?
edit: Foo and OtherObject are both meant to be classes whose constructors I'm defining.
You may use:
Foo(std::unique_ptr<Child> thing) :
OtherObject(std::move(thing)),
child_(OtherObject.getChildPtr()) /* one accessor to get the pointer. */
{}
If base object OtherObject doesn't provide an accessor to the pointer, you may delegate the constructor to an other constructor, something like:
class Foo: public OtherObject
{
public:
Foo(std::unique_ptr<Child> thing) : Foo(thing, thing.get()) {}
private:
Foo(std::unique_ptr<Child>& thing, Child* child) :
OtherObject(std::move(thing)),
child_(child)
{}
private:
Child* child_;
};
A third solution would be to change the order between OtherObject and child_ (to have child_ before) by introducing an other derivation:
class ChildPtr
{
public:
ChildPtr(Child* child) : child_(child) {}
Child* child_;
};
class Foo: private ChildPtr, public OtherObject
{
public:
Foo(std::unique_ptr<Child> thing) :
ChildPtr(thing.get()),
OtherObject(std::move(thing))
{}
};
Generally what happens is described in the standard as:
§17.6.5.15.1 Moved-from state of library types [lib.types.movedfrom]
Objects of types defined in the C++ standard library may be moved from (12.8). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
The standard actually specifically describe the behavior of std::unique_ptr in:
§20.8.1.4 Class template unique_ptr [unique.ptr]
Additionally,
ucan, upon request, transfer ownership to another unique pointeru2. Upon completion of such a transfer, the following postconditions hold:
- u2.p is equal to the pre-transfer u.p,
- u.p is equal to nullptr, and
- if the pre-transfer u.d maintained state, such state has been transferred to u2.d.
Specifically, dep, after the construction of the Foo sub-object at:
Foo(std::move(dep))
is a nullptr.
Moreover if dep was still a valid pointer, Foo(std::move(dep)) would have copied dep, which doesn't make sense for std::unique_ptr's semantic (as it is non-copyable).
What you want to do is let a reference to the pointed object, with the due considerations of the case (for example, can the unique_ptr be nullpt? etc..), in Foo:
class Foo {
public:
Foo(unique_ptr<Dep> dep) : dep_(dep) {}
const Dep& get_dep() const { return *dep_; }
Dep& get_dep() { return *dep_; }
private:
std::unique_ptr<Dep> dep_;
};
and then simply construct the TestFoo object as:
TestFoo(unique_ptr<MockDep> dep) : Foo(std::move(dep)) {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With