Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ std::unique_ptr stored inside std::map use of deleted function ill formed

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?

like image 881
ScaryAardvark Avatar asked Aug 28 '15 10:08

ScaryAardvark


1 Answers

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.

like image 164
edmz Avatar answered Nov 19 '22 02:11

edmz