How to create a vector of unique pointers pointing at default constructed objects

Is there a way to create a vector containing N number of std::unique_ptr<T>s? GCC v11.2 shows huge and cryptic error messages so I'm not able to detect the issue.

Here is an MRE of what I was trying to do:

#include <iostream>
#include <vector>
#include <memory>


// a dummy struct
struct Foo
{
    int m_value;
};


int main( )
{
    constexpr auto rowCount { 10uz };
    constexpr auto colCount { 20uz };

    // a 1D vector of pointers, does not compile
    std::vector< std::unique_ptr<Foo> > vec_1D( colCount, std::make_unique<Foo>( ) );

    for ( const auto& ptr : vec_1D )
    {
        std::cout << "Address: " << ptr << " --- value: " << ptr->m_value << '\n';
    }

    // a 2D vector of pointers, does not compile
    std::vector< std::vector< std::unique_ptr<Foo> > >
        matrix( rowCount, std::vector< std::unique_ptr<Foo> >( colCount, std::make_unique<Foo>( ) ) );

}

I think I'm missing something important about std::unique_ptr here. Is this error because of the fact that unique_ptr is not copy-constructible?

If the above method is not possible, then what could be the alternative?


The line:

std::vector< std::unique_ptr<Foo> > vec_1D( colCount, std::make_unique<Foo>( ) );

makes use of the following vector constructor:

     vector( size_type count,
             const T& value,
             const Allocator& alloc = Allocator());

Which receives a given value and copies it to every element of the vector. From cppreference:

  1. Constructs the container with count copies of elements with value value.

So you are calling std::make_unique<Foo>(), getting a std::unique_ptr<Foo>&& back from that call, and passing it to the std::vector's constructor so that it copies it across. The problem is that that unique pointer is not copyable.


You could though:

  • create a vector with a given size, and
  • for every element in the vector,
    • create a unique pointer (one at a time), and
    • move assign that unique pointer to the vector's element.

The example below uses std::generate to fill the vector. Notice that the generator function is returning a std::unique_ptr<Foo>&& that is move assignable to each vector's element.

[Demo]

#include <algorithm>  // generate
#include <iostream>  // cout
#include <memory>
#include <vector>

// a dummy struct
struct Foo
{
    Foo() : m_value{value++} {}
    static inline int value{};
    int m_value{};
};

int main( )
{
    const size_t count{ 20 };
    std::vector<std::unique_ptr<Foo>> v(count);
    std::generate(v.begin(), v.end(), []() { return std::make_unique<Foo>(); });
    for (auto&& up : v) { std::cout << up->m_value << " "; }
}

// Outputs
//
//   0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19