Reputation: 6488
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.
Upvotes: 5
Views: 445
Reputation: 217085
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))
{}
};
Upvotes: 6
Reputation: 76240
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,
u
can, 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)) {}
Upvotes: 1