Как передать 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
.