Подтвердить что ты не робот

unique_ptr для производного класса в качестве аргумента функции, которая переводит unique_ptr в базовый класс

Я пытаюсь использовать unique_ptr для производного класса в функции, которая переводит unique_ptr в базовый класс. Что-то вроде:

class Base {};

class Derived : public Base {};

void f(unique_ptr<Base> const &base) {}

…

unique_ptr<Derived> derived = unique_ptr<Derived>(new Derived);
f(derived);

Если я правильно понимаю этот ответ, этот код должен работать, но он вызывает следующие ошибки компиляции:

ошибка C2664: "f": невозможно преобразовать параметр 1 из "std :: unique_ptr <_Ty>" в "const std :: unique_ptr <_Ty> &"

IntelliSense: не существует подходящего пользовательского преобразования из "std :: unique_ptr <Derived, std :: default_delete <Derived >>" в "const std :: unique_ptr <Base, std :: default_delete <Base >>"

Если я изменю f на брать unique_ptr<Derived> const &derived, он будет работать нормально, но это не то, что я хочу.

Я делаю что-то неправильно? Что я могу сделать, чтобы обойти это?

Я использую Visual Studio 2012.

4b9b3361

Ответ 1

У вас есть три варианта:

  • Отказ от собственности. Это оставит вашу локальную переменную без доступа к динамическому объекту после вызова функции; объект был передан вызываемому лицу:

    f(std::move(derived));
    
  • Измените подпись f:

    void f(std::unique_ptr<Derived> const &);
    
  • Измените тип переменной:

    std::unique_ptr<base> derived = std::unique_ptr<Derived>(new Derived);
    

    Или, конечно, просто:

    std::unique_ptr<base> derived(new Derived);
    

    Или даже:

    std::unique_ptr<base> derived = std::make_unique<Derived>();
    
  • Обновление: Или, как рекомендовано в комментариях, вообще не передавать права собственности:

    void f(Base & b);
    
    f(*derived);
    

Ответ 2

Возможное решение состоит в том, чтобы изменить тип аргумента как Base const* и передать derived.get(). Передача права собственности с помощью unique_ptr const<Base>&unique_ptr не изменяется), поэтому переход на Base const* не меняет значения.


Herb Sutter обсуждает длинные аргументы интеллектуального указателя в Параметры интеллектуального указателя. Выдержка из связанной статьи относится к этой точной ситуации:

Передача const unique_ptr<widget>& странная, потому что она может принимать только null или widget, чье время жизни управляется в вызывающем коде через unique_ptr, и вызываемый обычно не должен заботиться о жизни вызывающих абонентов выбор управления. Передача widget* охватывает строгий суперсет этих случаев и может принимать "null или widget" независимо от политики времени жизни, которую использует вызывающий объект.

Ответ 3

У меня был вариант № 1 принятого ответа, и у меня все еще была та же ошибка компиляции. Больше часа я бился головой о стену и наконец понял, что

class Derived : Base {};

вместо

class Derived : public Base {};