4

I have a class Foo with a member variable of type std::vector<std::unique_ptr<Bar>>, which I would like to fill in the initialization list of the constructor of this class. Is this possible?

I was hoping that using the fill constructor of vector would be possible, something like this

Foo::Foo(int n):
    vector<unique_ptr<Bar>> (n, unique_ptr<Bar> (new Bar))
{}

but I think this requires copy constructor of std::unique_ptr, which is deleted (as it should be) (unique_ptr(const unique_ptr&) = delete).

Is there a better way to go about this?

Niall
  • 30,036
  • 10
  • 99
  • 142
Filip S.
  • 1,514
  • 3
  • 19
  • 40
  • 1
    You're right in that your attempt won't work because you only call `new Bar` once, whereas for a vector of unique pointers you need to call `new Bar` once for each element. I guess you are best off just using a loop with `emplace_back`. – M.M Jan 15 '16 at 06:54
  • Alternatively you can fill construct with empty `unique_ptr`s and then loop through calling `reset(new Bar)` on each one. – Jonathan Potter Jan 15 '16 at 07:01
  • Until C++ gets iterable coroutines, I think that your best bet is a loop. – zneak Jan 15 '16 at 07:12
  • @zneak never heard of 'iterable coroutines' before. have a link to an explanation? – Tom Knapen Jan 15 '16 at 09:12

1 Answers1

4

Since it is not copyable, move it!

Solution with hard-coded objects:

#include <memory>
#include <vector>
#include <iterator>
class Bar{};
class Foo{
    public:
    Foo():bars(get_bars()) {}
    std::vector<std::unique_ptr<Bar>> bars;

    private:
    std::vector<std::unique_ptr<Bar>> get_bars(){
        std::unique_ptr<Bar> inilizer_list_temp[]={std::make_unique<Bar>(),std::make_unique<Bar>(),std::make_unique<Bar>()};
        return  std::vector<std::unique_ptr<Bar>>{std::make_move_iterator(std::begin(inilizer_list_temp)),std::make_move_iterator(std::end(inilizer_list_temp))};
    }
};
int main()
{
    Foo foo;
}

Live Demo

Solution with Dynamic number of objects:

#include <memory>
#include <vector>
#include <iterator>
#include <iostream>
class Bar{
    public:
    int a=5;
    };
class Foo{
    public:
    Foo():bars(get_bars(10)) {}
    std::vector<std::unique_ptr<Bar>> bars;

    private:
    std::vector<std::unique_ptr<Bar>> get_bars(int n){
        std::vector<std::unique_ptr<Bar>> inilizer_list_temp;
        inilizer_list_temp.reserve(n);
        for(size_t i=0;i<n;++i){
            inilizer_list_temp.emplace_back(std::make_unique<Bar>());
        }
        return inilizer_list_temp;
    }
};
int main()
{
 Foo foo;
 for(auto const& item:foo.bars){
     std::cout << item->a;
 }
}

Live Demo

And see this for more details Can I list-initialize a vector of move-only type?

EDIT:

For C++11 users with no std::make_uniuqe:

template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
    return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}

Source

Community
  • 1
  • 1
Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
  • It seems to me that the point of the question is to avoid a loop and avoid typing out the unique_pointer creation logic for each instance. Your answer proposes hardcoding a collection of unique pointers and moving from it, which is fundamentally the same as filling out the vector by hand... – zneak Jan 15 '16 at 07:08
  • Works well with 2, what about 2000? – Jonathan Potter Jan 15 '16 at 07:11
  • This looks very promising. What if I don't have C++14 support? (I think std::make_unique is a C++14 feature). – Filip S. Jan 15 '16 at 09:14