Forthro
Forthro

Reputation: 33

How to create a map from initializer_list with non-copyable members in C++?

So, I have a map that stores objects of a class with deleted copy constructor, due to it having a unique_ptr as one of the members. I'm trying to construct this map using std::initializer_list, but I'm getting a huge error, ending with "use of deleted function 'constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&)". If I understand correctly, the cause of it is that list's members are not temporaries, but constant references instead, and therefore you just can't move them, neither in an implicit, nor in an explicit way.

Here is the part of the header:

class ExitMessage {
public:
    ...
    ExitMessage( const std::string& text, 
                 std::initializer_list<std::map<std::string, ExitMessage>::value_type> subMessages);
    ...
private:
    std::string _text;
    std::unique_ptr<std::map<std::string, ExitMessage>> _subMessages;
    ...
};

And this is the problematic constructor:

ExitMessage::ExitMessage( const std::string& text,
                          std::initializer_list<std::map<std::string, ExitMessage>::value_type> subMessages )
    : _text( text ), _subMessages( new std::map<std::string, ExitMessage>( subMessages ) ) { }

So, what can I do to make it possible to create an instances of this class using std::initializer_list?

Upvotes: 2

Views: 173

Answers (1)

Forthro
Forthro

Reputation: 33

So, I worked a bit on the constructor and managed to come up with the solution myself. As my objects are not copyable, there were no downsides to making a second argument of the initializer_list's pair an rvalue.

std::initializer_list<std::pair<const std::string, ExitMessage&&>>

However, now I couldn't use a default constructor for the map, so I had to write an initialization loop myself.

ExitMessage::ExitMessage( const std::string& text,
                          std::initializer_list<std::pair<const std::string, ExitMessage&&>>&& subMessages )
    : _text( text ) {
    _subMessages = std::make_unique<std::map<std::string, ExitMessage>>();

    for (const std::pair<const std::string, ExitMessage&&>& subMessage : subMessages) {
        auto& subMessageRef = const_cast<std::pair<const std::string, ExitMessage&&>&>(subMessage);
        _subMessages->emplace( subMessageRef.first, std::move( subMessageRef.second ) );
    }
}

As I'm now sure that parameter objects are either temporaries or explicitly moved (that was still a requirement before, due to a deleted copy constructor, just not for the list itself), I can safely unconst initializer_list's members and manually move them into the map.

Upvotes: 0

Related Questions