Правильный способ передачи права собственности на std::vector <std:: unique_ptr <int>> на построенный класс

Каков правильный способ передачи права собственности на std::vector<unique_ptr<int> > на построенный класс?

Ниже приведено представление кода того, что я хочу сделать. Я понимаю, что это неверно (не компилируется) и нарушает "уникальность", передаю ли я вектор конструктору по значению или по ссылке. Я хочу, чтобы Foo был новым владельцем вектора и хотел, чтобы вызывающая функция отказалась от владения. Мне нужен конструктор для выполнения std::unique_ptr<std::vector<std::unique_ptr<int> > > для этого?

foo.h

class Foo
{
public:
  Foo(vector<std::unique_ptr<int> > vecOfIntPtrsOwnedByCaller);

private:
  vector<std::unique_ptr<int> > _vecOfIntPtrsOwnedByFoo;
}

foo.cpp

Foo::Foo(std::vector<std::unique_ptr< int> > vecOfIntPtrsOwnedByCaller)
{
    _vecOfIntPtrsOwnedByFoo = vecOfIntPtrsOwnedByCaller;
}

Любая помощь будет высоко оценена - я прочесал сеть, ища правильный способ сделать это. Спасибо!

Ответы

Ответ 1

std::unique_ptr<T> - это не скопируемый, но подвижный тип. Наличие типа "только для перемещения" в std:vector<T> также делает перемещение std::vector<T> только для перемещения. Чтобы компилятор автоматически перемещал объекты, вам нужно иметь значение r для перемещения-перемещения или назначения перемещения. Внутри вашего конструктора объект vecOfIntPtrsOwnedByCaller является l-значением, хотя тот, который, несмотря на свое имя, уже владеет указателем на int s: они были украдены у вызывающего, когда вызывающий объект создал объект. Чтобы перейти от l-значения, вам нужно использовать std::move() (или что-то подобное):

Foo::Foo(std::vector<std::unique_ptr<int>> vecOfIntPtrsOwnedByCaller)
{
    _vecOfIntPtrsOwnedByFoo = std::move(vecOfIntPtrsOwnedByCaller);
}

или, предпочтительнее

Foo::Foo(std::vector<std::unique_ptr<int>> vecOfIntPtrsOwnedByCaller)
    : _vecOfIntPtrsOwnedByFoo(std::move(vecOfIntPtrsOwnedByCaller))
{
}

Последний подход позволяет избежать первого создания по умолчанию элемента, а затем назначить ему перенос и, вместо этого, перемещать элементы непосредственно. Я предполагаю, что я также сделаю аргумент ссылкой на r-значение, но это не обязательно.

Обратите внимание, что вы можете создавать объекты типа Foo только из того, что может быть привязано к r-значению, например:

int main() {
    Foo f0(std::vector<std::unique_ptr<int>>()); // OK
    std::vector<std::unique_ptr<int>> v;
    Foo f1(v); v// ERROR: using with an l-value
    Foo f2{v}; v// ERROR: using with an l-value
    Foo f3 = v; // ERROR: using with an l-value
    Foo f4(std::move(v)); // OK: pretend that v is an r-value
}