8

I have the following code which won't compile and it's Friday and I'm a bit frazzled.

#include <string>
#include <memory>
#include <utility>
#include <map>

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

struct A
{
};

std::map< std::string, std::unique_ptr< A > > _map = { { "A", make_unique< A >() } }; // <-- ERROR!!

The following compiles without a problem

int main()
{
    std::pair< std::string, std::unique_ptr< A > > p { "B", make_unique< A >() };
    _map.insert( std::make_pair( "C", make_unique< A >() ) );
}

The error I'm getting is (roughly, as removed g++ fluff)

use of deleted function 'constexpr std::pair<...>( const st::pair<...> & )
'constexp std::pair<...>::pair( const std::pair<...> & ) is implicitly deleted because the default definition would be illegal.

Argghh!! Just read this in the c++11 standard.

When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause

bummer!!!

Anyone know if it's just plain impossible with initialiser lists?

ScaryAardvark
  • 2,855
  • 4
  • 30
  • 43
  • I really need this to work with the initialiser lists. The code which compiles was just for example. – ScaryAardvark Aug 28 '15 at 10:20
  • Hmm. Just wondering. Do initialiser_lists require their arguments to be copyable? In which case, the error would make sense as you can't copy a unique_ptr – ScaryAardvark Aug 28 '15 at 10:23
  • 3
    [This](http://stackoverflow.com/questions/8193102/initializer-list-and-move-semantics) seems relevant.... – Tony Delroy Aug 28 '15 at 10:38

1 Answers1

5

You can't do much about it: elements in an initializer list are copied. This will not get along with classes that are move-only.

There's a way to bypass this "defect" but it isn't very nice to read; you decide

using map_type  = std::map< std::string, std::unique_ptr< A > >;
using pair_type = map_type::value_type;
pair_type elements[] = { { "A", std::make_unique< A >() }, { "B", std::make_unique< A >() } };

map_type myMap { std::make_move_iterator( begin(elements) ), std::make_move_iterator( end(elements) ) };

which will make myMap iterate over the range and move the elements within, rather than copying. Method kindly taken from this other question.

edmz
  • 8,220
  • 2
  • 26
  • 45
  • That's a good way of getting around it but it is, like you say, not nice. However, as it solves the problem I have at the moment, you get the gold star :o) – ScaryAardvark Aug 28 '15 at 11:00