Передать необработанный указатель в функцию, которая принимает unique_ptr в качестве параметра - PullRequest
0 голосов
/ 02 июля 2018

Я пытаюсь написать функцию, которая принимает unique_ptr к классам, полученным из Base в качестве параметров (для указания передачи права собственности). Я придумал следующий код:

#include <memory>
#include <type_traits>

class Base
{
  public:
    Base() {}
};

class Derived : public Base
{
  public:
    Derived() {}
};


void foo(std::unique_ptr<Derived> _data) {}

template <class T>
void boo(std::unique_ptr<T> _data) {
    static_assert(std::is_convertible<T*, Base*>::value,
                  "Only classes derived from Base can be used as "
                  "parameter for boo");
}


int main() { foo(new Derived); }

, но когда я пытаюсь скомпилировать его, я получаю сообщение об ошибке, сообщающее мне, что could not convert ‘(operator new(1ul), (<statement>, ((Derived*)<anonymous>)))’ from ‘Derived*’ to ‘std::unique_ptr<Derived>’, если я использую foo и то же самое (плюс некоторые детали шаблона), когда я использую boo. Если я все правильно понял, то мне говорят, что для std::unique_ptr<Derived> нет конструктора, который мог бы принять указатель на Derived. Но когда я посмотрел на cppreference page конструктор unqiue_ptr, я увидел, что такой конструктор есть (номер 3-4).

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

P.S. Я пытаюсь написать функцию, которая принимает unique_ptr, потому что я хочу указать, что право собственности на параметр будет передано. Если вы знаете, как написать функцию с подписью, которая может ясно и однозначно показать, что право собственности передается, я бы также принял это как ответ. Эта функция может использовать шаблон static_assert или указатель Base в качестве параметра.

P.S. Основная проблема в том, как сделать foo(new Derived). Я знаю, что могу использовать boo(std::make_unique<Derived>()) (в этом случае все работает), но я также действительно хочу знать, почему не работает пример с необработанным указателем, потому что я не знаю, как указать, что я ' m «крадет» владение для необработанного указателя.

P.S Пример использования (с необработанными указателями) ( НЕ РАБОТАЕТ )

Derived* derived_1 = new Derived;
// Do something with derived_1
boo(derived_1); // Pass to function and don't worry about `delete`
                // because you know that ownership has been
                // transferred and no longer is your concern

И с умными указателями

void foo_2(std::unique_ptr<Derived>& _data) {
     boo(std::move(_data)); 
}

std::unique_ptr<Derived> derived_2 = std::make_unique<Derived>();
// Do something with derived_2
foo(std::move(derived_2)); // Have to use `std::move`
                           // or
foo_2(derived_2);

Второй (исключая необходимость в новой функции) не так прост, как первый (хотя я должен признать, что разница не так уж велика (возможно, 2-кратный ввод))

Ответы [ 2 ]

0 голосов
/ 02 июля 2018

Вы должны быть явным в своем преобразовании:

int main()
{
    Derived* d = nullptr;
    BadInterface(&d); // mainly d = new Derived(..);
    foo(std::unique_ptr<Derived>(d));
}

, что дает в вашем случае один вкладыш:

foo(std::unique_ptr<Derived>(new Derived));
0 голосов
/ 02 июля 2018

Перегрузки 3 и 4 здесь не применяются. Их подпись

unique_ptr( pointer p, /* see below */ d1 ) noexcept;

unique_ptr( pointer p, /* see below */ d2 ) noexcept;

и они принимают второй параметр, который является пользовательским средством удаления. Единственная перегрузка, которая применяется, является 2, который

explicit unique_ptr( pointer p ) noexcept;

Поскольку он помечен как явный, он неявным образом не преобразует указатель в unique_ptr. Это вопросы безопасности, и правильно сделать это явно. Если это не было explicit, тогда

void foo(std::unique_ptr<int> _data) {}

int main()
{
    int bar;
    foo(&bar);
}

будет компилироваться, но это будет неопределенное поведение, так как _data будет пытаться удалить указатель, когда он будет уничтожен в конце foo.

...