Vector<Unique_Ptr<A> > Using Initialization List

Initializing container of unique_ptrs from initializer list fails with GCC 4.7

unique_ptr's constructor is explicit. So you can't create one implicitly with from new string{"foo"}. It needs to be something like unique_ptr<string>{ new string{"foo"} }.

Which leads us to this

// not good
vector<unique_ptr<string>> vs {
unique_ptr<string>{ new string{"Doug"} },
unique_ptr<string>{ new string{"Adams"} }
};

However it may leak if one of the constructors fails. It's safer to use make_unique:

// does not work
vector<unique_ptr<string>> vs {
make_unique<string>("Doug"),
make_unique<string>("Adams")
};

But... initializer_lists always perform copies, and unique_ptrs are not copyable. This is something really annoying about initializer lists. You can hack around it, or fallback to initialization with calls to emplace_back.

If you're actually managing strings with smart pointers and it's not just for the example, then you can do even better: just make a vector<string>. The std::string already handles the resources it uses.

vectorunique_ptrA using initialization list

Initialization lists are wrappers around const arrays.

unique_ptrs that are const cannot be moved-from.

We can hack around this (in a perfectly legal way) like this:

template<class T>
struct movable_il {
mutable T t;
operator T() const&& { return std::move(t); }
movable_il( T&& in ): t(std::move(in)) {}
};

template<class T, class A=std::allocator<T>>
std::vector<T,A> vector_from_il( std::initializer_list< movable_il<T> > il ) {
std::vector<T,A> r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
return r;
}

Live example.

Use:

auto v = vector_from_il< std::unique_ptr<int> >({
std::make_unique<int>(7),
std::make_unique<int>(3)
});

If you want to know why initializer lists reference const data, you'll have to track down and read committee minutes or ask someone who was there. I'd guess it is about the principle of least surprise and/or people with bugaboos about mutable data and view types (such as the renaming of array_view to span).

If you want more than just vectors:

template<class C, class T=typename C::value_type>
C container_from_il( std::initializer_list< movable_il<T> > il ) {
C r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
return r;
}

which still needs massaging to work right with associative containers as we also want to move the key.

template<class VT>
struct fix_vt {
using type=VT;
};
template<class VT>
using fix_vt_t = typename fix_vt<VT>::type;
template<class VT>
struct fix_vt<const VT>:fix_vt<VT>{};
template<class K, class V>
struct fix_vt<std::pair<K,V>>{
using type=std::pair<
typename std::remove_cv<K>::type,
typename std::remove_cv<V>::type
>;
};

template<class C, class T=fix_vt_t<typename C::value_type>>
C container_from_il( std::initializer_list< movable_il<T> > il ) {
C r( std::make_move_iterator(il.begin()), std::make_move_iterator(il.end()) );
return r;
}

vectorunique_ptrBase using initialization list of Derived

Adding extra constructor

template <typename U>
movable_il(U&& in): t(std::forward<U>(in)) {}

fixes compilation.

Demo.

Any way to initialize a vector of unique_ptr?

You can't move from an initializer list because the elements are const. §8.5.4 [dcl.init.list]/p5:

An object of type std::initializer_list<E> is constructed from an
initializer list as if the implementation allocated an array of N
elements of type const E, where N is the number of elements in the
initializer list. Each element of that array is copy-initialized with
the corresponding element of the initializer list, and the
std::initializer_list<E> object is constructed to refer to that
array.

You can only copy, but you can't copy a unique_ptr since it's move-only.

You'd have to use push_back or emplace_back etc. to fill the vector after you construct it.

C++ - construct vector of unique_ptr via initialization list

You can make an initialization list like this:

#include<memory>

class base{};

class derived1 : public base{};
class derived2 : public base{};;
class derived3 : public base{};;

using namespace std;
int main (){
auto ptrs={ unique_ptr<base>(new derived1()) , unique_ptr<base>(new derived2()) , unique_ptr<base>(new derived3()) };

}

But the problem seems to be that you can not move from that initializer list since initializer lists are constant. There is more on this here. initializer_list and move semantics

I asked a related but different question at in-place vector construction from initialization list (for class with constructor arguments)

Creating list of unique_ptr using initialization list and make_unique fails in GCC 5.4

You can use:

const std::initializer_list<std::unique_ptr<int>> list{
std::make_unique< int >( 42 ),
std::make_unique< int >( 0 ),
std::make_unique< int >( 0 )
};

Demo (old gcc-5.4 tested).

Initializing container of unique_ptr's using initialization list, continued

The main problem, as far as I see it, is that you need to make the pair's movable. This is perfectly done by the movable_il class, but now you get into trouble trying to construct the map from these, as the first and second members are not defined for movable_il.

As you are anyways trying to construct the map from an iterator, I don't imagine you'll see any performance hit by doing the iteration yourself and inserting the elements manually. I've made a solution based on this. It's not the prettiest solution, but It'll do the job.

Furthermore I had some issues to get the compiler to deduce the correct type for T from the pair "initialization list", thus I made a small helper function movable_pair to create these, in which case the compiler has no problems.

#include <memory>
#include <map>
#include <utility>
#include <iostream>
#include <vector>

// Wrapper class to make type T "movable"
template<class T>
struct movable_il
{
mutable T t;
operator T() const&& { return std::move(t); }
movable_il( T&& in ): t(std::move(in)) {}

T get() const { return std::move(t); }
};

// Some template magic to deduce the correct value_type
// ( not really needed for this example as we are mostly interested in maps )
template<class VT>
struct fix_vt
{
using type = VT;
};

template<class VT>
using fix_vt_t = typename fix_vt<VT>::type;

template<class VT> struct fix_vt<const VT> : fix_vt<VT> {};

template<class K, class V>
struct fix_vt< std::pair<K,V> >
{
using type = std::pair<
typename std::remove_cv<K>::type,
typename std::remove_cv<V>::type
>;
};

// Create map from initializer list of movable T (pairs)
template<class C, class T = fix_vt_t<typename C::value_type> >
auto map_from_il(std::initializer_list< movable_il<T> > il)
{
using map_type = C;
auto map = map_type{};

// Loop over il list and insert each element into the map
for(auto&& entry : il)
{
map.insert(std::move(entry.get())); // We get the pair from the movable class and insert it by moving
}

return map;
}

// Helper function to create movable pair
template<class First, class Second>
auto movable_pair(First&& f, Second&& s)
{
using pair_type = std::pair<First, Second>;
return movable_il<pair_type>{ pair_type{ std::forward<First>(f), std::forward<Second>(s) } };
}

// Main function
int main()
{
using iptr = std::unique_ptr<int>;
using key_type = std::string;
using value_type = iptr;

using map_type = std::map<key_type, value_type>;

auto lol = map_from_il<map_type>({ movable_pair("a", iptr{ new int {3} } ), movable_pair("b", iptr{ new int {2} }) });

// Small print-out to check we inserted the correct elements :)
for(auto& l : lol)
{
std::cout << l.first << " " << *l.second << std::endl;
}

return 0;
}

Disclaimer: I have only tested this with GCC 8.1.0 (but I don't imagine any issues using other compilers).

Update: If you put in and extra set of { and } when passing the pairs to map_from_il, the code compiles without the use of the movable_pair helper function.

auto lol = map_from_il<map_type>({ { {"a", iptr{ new int {3} } } }, { {"b", iptr{ new int {2} } } } });

Update 2: If you add the following constructor to movable_il, the code also compiles without the extra { and }:

template<class... U>
movable_il( U&&... in): t{std::forward<U>(in)...} {}

With this you can write:

auto lol = map_from_il<map_type>({ {"a", iptr{ new int {3} } }, {"b", iptr{ new int {2} } } });

using an initializer list where the values contain unique_ptr member variables

It's fault of std::initializer_list, if you look at it's begin/end member functions, they return const T*, which means it will force std::map to try use the copy constructor of your Foo, which is deleted as std::unique_ptr can not be copied.

This issue is not unique to std::map, any container which allows you to initialize it with std::initializer_list will really copy the arguments from an initializer list.

The C++ standard requires that during such initialization there's a temporary const T[N] array, which the std::initializer_list points to, const disables moves from it.

C++11 initializer list with unique_ptr

Do you care particularly about using initializer lists?

If your goal is just to create the vector above, you can use the following syntax:

std::vector<std::unique_ptr<Thing>>  WestOfStartThings;
WestOfStartThing.emplace_back(new Spider);

If you do want to use the initializer lists specifically, I believe the syntax is:

std::vector<std::unique_ptr<Thing>>  stuff{std::unique_ptr<Thing>{new Spider}};

creating the unique_ptr with an initialzer rather than the constructor.



Related Topics



Leave a reply



Submit