..

std::map更新自定义value的问题

简化的描述下项目中的需求,有一个功能,需要合并两个vector A 和 vector B 中的数据, 如果B中的数据是A中没有的,则直接加入到A中,否则,更新A中对应的数据。 数据结构如下:

struct Data {
    int type;
    std::string account;
    std::string d;

    Data(int t, std::string a, std::string d) {
        this->type = t;
        this->a    = a;
        this->d    = d;
    }
};

其他, type和account是唯一的,可以用来做 map的key。

为什么 std::pair可以用来做 std::map 的key ? 因为 std::pair 满足 std::map 中关于 key 的设定。 std::map 关于 key 的描述如下:

template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
           > class map;

key 的Compare默认是实现 less , std::pair 就实现了对 less 的重载。 因此可用。 【TIP】 std::unordered_map 的 key 的要求如下:

template < class Key,                                    // unordered_map::key_type
           class T,                                      // unordered_map::mapped_type
           class Hash = hash<Key>,                       // unordered_map::hasher
           class Pred = equal_to<Key>,                   // unordered_map::key_equal
           class Alloc = allocator< pair<const Key,T> >  // unordered_map::allocator_type
           > class unordered_map;

需要 实现 hash。

使用 std::map insert/emplace方法。

using key_type = std::pair<int, std::string>;
using map_type = std::map<key_type, Data>;
map_type map;

for (auto data : A) {
    key_type key(data.type, data.a);
    map.insert(key, data);
}

有问题! 有重复的key,插入不会成功。insert/emplace, 这两个方法在插入的时候会返回插入结果, std::pair形式。 return value 描述:

If the insertion takes place (because no other element existed with the same key), the function returns a pair object, whose first component is an iterator to the inserted element, and whose second component is true.

Otherwise, the pair object returned has as first component an iterator pointing to the element in the container with the same key, and false as its second component.

Member type iterator is a forward iterator type.

map中的key是唯一的,这就造成了insert/emplace不成功的问题。这样A中的数据不能从B中更新了。

使用 operator[] 的方式

for (auto data : A) {
    key_type key(data.type, data.a);
    map[key] = data;
}

编译错误大致如下:

/usr/include/c++/4.9/tuple:1104:70: error: no matching function for call to 'main()::Data::Data()'
         second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)

分析一下, 看一个 std::map 中 对 operator[] 的描述。

1) Inserts value_type(key, T()) if the key does not exist. This function is equivalent to return insert(std::make_pair(key, T())).first->second;
-key_type must meet the requirements of CopyConstructible.
-mapped_type must meet the requirements of CopyConstructible and DefaultConstructible.
If an insertion is performed, the mapped value is value-initialized (default-constructed for class types, zero-initialized otherwise) and a reference to it is returned.

这样就明白了, 当前key不存在的时候, 会调用 T() 来构造对象, 但是对于 Data来说, 已经有了一个带参数的 constructor, 编译器就不会再生成 Data() constructor, 因此,导致了上述编译错误。

解决

添加一个 default contructor .

Data(){}

参考

std::pair
std::map
std::unordered_map
operator []