Семантика владения unique_ptr

Возможно, я пытался быть слишком общим. (Оригинальный вопрос ниже) Конкретно, у меня есть некоторая зависимость Dep класса Foo, У меня тоже есть класс MockDep и я определяю класс TestFoo, Вот его конструктор, который я попытался написать:

TestFoo(unique_ptr<MockDep> dep) : Foo(std::move(dep)), mock_dep_(dep.get()) {}

А также FooКонструктор выглядит так:

Foo(unique_ptr<Dep> dep) : dep_(dep) {}

mock_dep_ Delcared в TestFoo как MockDep* mock_dep_, а также dep_ объявлен в Foo как unique_ptr<Dep> dep_, Как я могу получить mock_dep_ содержать dep_адрес? (так как выше не работает, так как std::move(dep) обнуляет dep.)


Исходное сообщение:

У меня есть объект типа Foo что я должен передать другому объекту типа OtherObject который претендует на владение им, но как указатель на его базовый класс. Тем не менее, я хочу получить указатель на дочерний объект, который я могу использовать для ссылки на него. Я написал что-то вроде:

Foo(std::unique_ptr<Child> thing) :
    OtherObject(std::move(thing)), child_(thing.get()) {}

OtherObject(std::unique_ptr<Base> thing, ...) { ... }

Тем не менее, это не похоже на работу, так как std::move(thing) кажется, обнулить указатель, который возвращается из thing.get() потом.

я могу изменить Fooпараметр, чтобы иметь тип Child* вместо unique_ptr<Child>, но я предпочел бы иметь возможность сделать последнее, поскольку оно явно документирует семантику владения.

Какой самый подходящий (или неудачный, ненавязчивый) способ решения этой проблемы?

редактировать: Foo а также OtherObject оба должны быть классами, чьи конструкторы я определяю.

2 ответа

Вы можете использовать:

Foo(std::unique_ptr<Child> thing) :
    OtherObject(std::move(thing)),
    child_(OtherObject.getChildPtr()) /* one accessor to get the pointer. */
{}

Если базовый объект OtherObject не предоставляет средства доступа к указателю, вы можете делегировать конструктор другому конструктору, например:

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_;
};

Третьим решением было бы изменить порядок между OtherObject а также child_ (иметь child_ до) путем введения другого деривации:

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))
    {}
};

Обычно то, что происходит, описано в стандарте как:

§17.6.5.15.1 Состояние перемещенных типов библиотек [lib.types.movedfrom]

Объекты типов, определенных в стандартной библиотеке C++, могут быть перемещены из (12.8). Операции перемещения могут быть явно указаны или неявно сгенерированы. Если не указано иное, такие перемещенные объекты должны быть помещены в допустимое, но неопределенное состояние.

Стандарт на самом деле конкретно описывает поведение std::unique_ptr в:

§20.8.1.4 Шаблон класса unique_ptr [unique.ptr]

Дополнительно, u может, по запросу, передать право собственности другому уникальному указателю u2, После завершения такого перевода выполняются следующие постусловия:

  • u2.p равен предварительной передаче вверх,
  • up равен nullptr, и
  • если состояние предварительной передачи ud сохранялось, то такое состояние было передано в u2.d.

В частности, depпосле строительства Foo подобъект в:

Foo(std::move(dep))

это nullptr,

Более того, если dep был еще действительным указателем, Foo(std::move(dep)) скопировал бы depчто не имеет смысла для std::unique_ptrсемантическая (как не копируемая).

То, что вы хотите сделать, это позволить ссылку на указанный объект с учетом обстоятельств дела (например, может ли unique_ptr быть nullpt? и т. д.), в 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_;
};

а затем просто построить TestFoo объект как:

TestFoo(unique_ptr<MockDep> dep) : Foo(std::move(dep)) {}
Другие вопросы по тегам