Заполнение вектора unique_pointers - PullRequest
2 голосов
/ 16 июня 2020

У меня есть классы A и B.
B происходит от A.
У меня также есть вектор std::vector<std::unique_ptr<A>> samples

Этот фрагмент кода работает:

std::vector<std::unique_ptr<A>> samples;
samples.push_back(std::make_unique<B>(param_1, param_2));

А вот этого нет:

std::vector<std::unique_ptr<A>> samples = {std::make_unique<B>(param_1, param_2)};

и выдает такую ​​ошибку:

/usr/include/c++/9/bits/stl_uninitialized.h: In instantiation of ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const std::unique_ptr<A>*; _ForwardIterator = std::unique_ptr<A>*]’:
/usr/include/c++/9/bits/stl_uninitialized.h:307:37:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const std::unique_ptr<A>*; _ForwardIterator = std::unique_ptr<A>*; _Tp = std::unique_ptr<A>]’
/usr/include/c++/9/bits/stl_vector.h:1582:33:   required from ‘void std::vector<_Tp, _Alloc>::_M_range_initialize(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const std::unique_ptr<A>*; _Tp = std::unique_ptr<A>; _Alloc = std::allocator<std::unique_ptr<A> >]’
/usr/include/c++/9/bits/stl_vector.h:626:2:   required from ‘std::vector<_Tp, _Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = std::unique_ptr<A>; _Alloc = std::allocator<std::unique_ptr<A> >; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<std::unique_ptr<A> >]’
my_file.cpp:88:113:   required from here
/usr/include/c++/9/bits/stl_uninitialized.h:127:72: error: static assertion failed: result type must be constructible from value type of input range
  127 |       static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
      |                                                                        ^~~~~
make: *** [makefile:15: cpp] Error 1

Вопросы
1. Связана ли эта ошибка с отсутствием правильного конструктора в vector (interator?) или в A/B классе?
2. Когда я делаю push_back, должен ли я всегда делать std:move с unique_pointer или нет? Спрашиваю, потому что сомневаюсь, может быть, без std::move неявно создаются какие-то копии. С другой стороны, может быть, компилятор произведет некоторые оптимизации и сможет распознать такую ​​«короткую» конструкцию?

Ответы [ 2 ]

1 голос
/ 16 июня 2020
  1. Связана ли эта ошибка с отсутствием правильного конструктора в vector (interator?) Или в A/B классе?

Ни то, ни другое, это происходит из-за того, что конструктор копирования std::unique_ptr неявно удаляется (из-за объявленного пользователем конструктора перемещения); вы не можете скопировать std::unique_ptr, вы переместите его.

Когда я делаю push_back, всегда ли std:move делать с unique_pointer или нет? Спрашиваю, потому что сомневаюсь, может быть, без std::move неявно создаются какие-то копии. С другой стороны, возможно, компилятор выполнит некоторые оптимизации и сможет распознать такую ​​"короткую" конструкцию?

Да, при вызове push_back вектора из std::unique_ptr элементов с аргументом lvalue необходимо использовать std::move для вызова конструктора перемещения std::unique_ptr (а не его неявно удаленного конструктора копирования).

#include <memory>
#include <vector>

int main() {
    std::vector<std::unique_ptr<int>> v;
    auto value = std::make_unique<int>(1);

    // v.push_back(value);                  // error (attempts to copy)
    v.push_back(std::move(value));          // lvalue "std::to_xvalue" -> move ctor
    v.push_back(std::make_unique<int>(2));  // already an rvalue -> move ctor
    return 0;
}
1 голос
/ 16 июня 2020
  1. Связана ли эта ошибка с отсутствием правильного конструктора в векторе?

Это одна интерпретация.

(interator?)

No.

или в классе A / B?

No.

Проблема в том, что конструктор вектора который вы используете, принимает std::initializer_list. Этот класс копирует аргументы. Уникальные указатели не копируются, поэтому это не работает.

Когда я делаю push_back, должен ли я всегда делать std: move с unique_pointer или нет?

Учитывая выражение lvalue, если вы хотите переместить его в вектор, вы должны используйте std:move. Вам не нужно и не следует использовать std:move с выражениями, которые уже являются r-значениями.

...