std::map<>::insert using non-copyable objects and uniform initialization

后端 未结 2 1432
慢半拍i
慢半拍i 2020-12-06 10:43

Have a look at the following code:

#include 
#include 

// non-copyable but movable
struct non_copyable {
    non_copyable() = defa         


        
相关标签:
2条回答
  • 2020-12-06 11:11

    Besides other answers of providing move (assignment) constructor, you could also store the non-copyable object through pointer, especially unique_ptr. unique_ptr will handle resource movement for you.

    0 讨论(0)
  • 2020-12-06 11:12

    [This is a complete rewrite. My earlier answer had nothing to do with the problem.]

    The map has two relevant insert overloads:

    • insert(const value_type& value), and

    • <template typename P> insert(P&& value).

    When you use the simple list-initializer map.insert({1, non_copyable()});, all possible overloads are considered. But only the first one (the one taking const value_type&) is found, since the other doesn't make sense (there's no way to magically guess that you meant to create a pair). The first over­load doesn't work of course since your element isn't copyable.

    You can make the second overload work by creating the pair explicitly, either with make_pair, as you already described, or by naming the value type explicitly:

    typedef std::map<int, non_copyable> map_type;
    
    map_type m;
    m.insert(map_type::value_type({1, non_copyable()}));
    

    Now the list-initializer knows to look for map_type::value_type constructors, finds the relevant mova­ble one, and the result is an rvalue pair which binds to the P&&-overload of the insert function.

    (Another option is to use emplace() with piecewise_construct and forward_as_tuple, though that would get a lot more verbose.)

    I suppose the moral here is that list-initializers look for viable overloads – but they have to know what to look for!

    0 讨论(0)
提交回复
热议问题