rainbow
rainbow

Reputation: 1301

Filling of vector with unique_pointers

I have two classes A and B.
B derives from A.
I have also vector std::vector<std::unique_ptr<A>> samples

This piece of code works:

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

But this one doesn't:

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

and generates such error:

/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

Questions

  1. Is this error connected with lack of proper constructor in vector (interator?) or in A/B class?
  2. When I make push_back, should I do always std:move with unique_pointer or not? I ask because I have doubts if maybe without std::move there are created implicitly some copies. In other hand maybe compiler does some optimalizations and can recognize such "short" construction?

Upvotes: 4

Views: 3100

Answers (2)

dfrib
dfrib

Reputation: 73206

  1. Is this error connected with lack of proper constructor in vector (interator?) or in A/B class?

Neither, this is due to the fact that the copy constructor of std::unique_ptr is implicitly deleted (due to the user-declared move constructor); you cannot copy a std::unique_ptr, you move it.

  1. When I make push_back, should I do always std:move with unique_pointer or not? I ask because I have doubts if maybe without std::move there are created implicitly some copies. In other hand maybe compiler does some optimalizations and can recognize such "short" construction?

Yes, when invoking push_back on a vector of std::unique_ptr elements with an lvalue argument, you need to use std::move to invoke the move constructor of std::unique_ptr (and not its implicitly deleted copy constructor).

#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;
}

Upvotes: 3

eerorika
eerorika

Reputation: 238441

  1. Is this error connected with lack of proper constructor in vector

That's one interpretation.

(interator?)

No.

or in A/B class?

No.

The issue is that the vector constructor that you use, accepts a std::initializer_list. That class copies the arguments. Unique pointers are not copiable, so this does not work.

  1. When I make push_back, should I do always std:move with unique_pointer or not?

Given an lvalue expression, if you want to move that into the vector, then you must use std:move. You don't need to, and shouldn't use std:move with expressions that are already rvalues.

Upvotes: 2

Related Questions