Как передать std :: unique_ptr в функцию

Как передать std::unique_ptr в функцию? Допустим, у меня есть следующий класс:

class A
{
public:
    A(int val)
    {
        _val = val;
    }

    int GetVal() { return _val; }
private:
    int _val;
};

Не компилируется следующее:

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);

    return 0;
}

Почему я не могу передать std::unique_ptr в функцию? Наверняка это основная цель конструкции? Или комитет C++ намеревался меня вернуться к необработанным указателям стиля C и передать его так:

MyFunc(&(*ptr)); 

И самое странное из всех, почему это OK способ его передачи? Кажется, это ужасно непоследовательно:

MyFunc(unique_ptr<A>(new A(1234)));

Ответ 1

Здесь в основном два варианта:

Передайте интеллектуальный указатель по ссылке

void MyFunc(unique_ptr<A> & arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);
}

Переместите интеллектуальный указатель в аргумент функции

Заметим, что в этом случае утверждение будет выполнено!

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(move(ptr));
    assert(ptr == nullptr)
}

Ответ 2

Вы передаете его по значению, что подразумевает создание копии. Это было бы не очень уникально, не так ли?

Вы можете перенести значение, но это означает передачу права собственности на объект и управление его временем жизни.

Если срок жизни объекта гарантированно будет существовать в течение всего срока службы вызова MyFunc, просто передайте необработанный указатель через ptr.get().

Ответ 3

Почему я не могу передать unique_ptr в функцию?

Вы не можете этого сделать, потому что unique_ptr имеет конструктор перемещения, но не конструктор копирования. Согласно стандарту, когда конструктор перемещения определен, но конструктор копирования не определен, конструктор копирования удаляется.

12.8 Копирование и перемещение объектов класса

...

7 Если определение класса явно не объявляет конструктор копии, одно объявляется неявно. Если определение класса объявляет конструктор перемещения или перемещает оператор присваивания, неявно объявленный конструктор копирования определяется как удаленный;

Вы можете передать unique_ptr функции, используя:

void MyFunc(std::unique_ptr<A>& arg)
{
    cout << arg->GetVal() << endl;
}

и используйте его, как и у вас:

или же

void MyFunc(std::unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

и используйте его так:

std::unique_ptr<A> ptr = std::unique_ptr<A>(new A(1234));
MyFunc(std::move(ptr));

Важная заметка

Обратите внимание, что если вы используете второй метод, ptr не имеет права собственности на указатель после возврата вызова std::move(ptr).

void MyFunc(std::unique_ptr<A>&& arg) будет иметь тот же эффект, что и void MyFunc(std::unique_ptr<A>& arg) поскольку оба являются ссылками.

В первом случае ptr все еще имеет право собственности на указатель после вызова MyFunc.

Ответ 4

Почему я не могу передать unique_ptr в функцию?

Вы можете, но не копировать, потому что std::unique_ptr<> не является конструктивным.

Наверняка это основная цель конструкции?

Помимо всего прочего, std::unique_ptr<> предназначен для однозначной маркировки уникальной собственности (в отличие от std::shared_ptr<>).

И самое странное из всех, почему это OK способ его передачи?

Потому что в этом случае нет конструкции копирования.

Ответ 5

Поскольку MyFunc не получает права собственности, было бы лучше иметь:

void MyFunc(const A* arg)
{
    assert(arg != nullptr); // or throw ?
    cout << arg->GetVal() << endl;
}

или лучше

void MyFunc(const A& arg)
{
    cout << arg.GetVal() << endl;
}

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

std::unique_ptr<A> ptr = std::make_unique<A>(1234);
MyFunc(std::move(ptr));

или передайте непосредственно ссылку на r-значение:

MyFunc(std::make_unique<A>(1234));

std::unique_ptr не имеет копии, чтобы гарантировать наличие только одного владельца.

Ответ 6

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

MyFunc(move(ptr));

Но после этого состояние ptr в main будет nullptr.