向映射中插入元素的推荐方法

114

可能是重复问题:
在 STL map 中,使用 map::insert 还是 [] 更好?

我想知道,当我向 map 中插入元素时,应该采用哪种推荐的方式。我应该:

map[key] = value;
或者
map.insert(std::pair<key_type, value_type>(key, value));

我进行了以下简单测试:

#include <map>
#include <string>
#include <iostream>

class Food {
public:
    Food(const std::string& name) : name(name) { std::cout << "constructor with string parameter" << std::endl; }
    Food(const Food& f) : name(f.name) { std::cout << "copy" << std::endl; }
    Food& operator=(const Food& f) { name = f.name; std::cout << "=" << std::endl; return *this; } 
    Food() { std::cout << "default" << std::endl; }
    std::string name;
};

int main() {
    std::map<std::string, Food> m0;

/*
1) constructor with string parameter
2) copy
3) copy
4) copy
*/
    m0.insert(std::pair<std::string, Food>("Key", Food("Ice Cream")));

/*
1) constructor with string parameter
2) default
3) copy
4) copy
5) =
*/
    // If we do not provide default constructor.
    // C2512: 'Food::Food' : no appropriate default constructor available
    m0["Key"] = Food("Ice Cream");
}
  1. 我意识到使用成员函数 insert 将涉及更少的函数调用,因此,使用 insert 是推荐的方法吗?
  2. 当使用 map [key] = value 方式时,为什么需要默认构造函数?

我知道 insert 不会覆盖已存在的键值对,但 map [key] = value 会。然而,在尝试从两者中选择时,这是否是我考虑的唯一因素呢?

如何处理以下因素?

  1. 性能
  2. 值默认构造函数的可用性
  3. ???

4
除非你能看到有严重的性能损失,否则何必费心敲打第一个选项卡?此外,一个不错的优化器可能会消除一些拷贝。 - stijn
1
为什么不这样看待它:如果您从不期望重复值,请使用insert,否则请使用map[key] = value - Ioan Paul Pirau
4个回答

84
  1. insert 不是推荐的方法之一,而是用来向 map 容器中插入元素的一种方式。与 operator[] 的区别在于,insert 可以告知元素是否成功插入到 map 容器中。此外,如果你的类没有默认构造函数,那么你必须使用 insert 方法。
  2. operator[] 需要默认构造函数,因为 map 容器会检查元素是否存在。如果不存在,则会使用默认构造函数创建一个元素并返回其引用(或常量引用)。

由于 map 容器不允许有重复的键值,插入操作会检查每个被插入的元素,判断容器中是否已存在相同键值的元素,如果是,则该元素不会被插入,并且它的映射值不会发生任何改变。


20
如果一个元素已经存在,"insert" 操作不会有任何变化,这一点在原文中没有提到,但却是最重要的区别。 - Sverre Rabbelier
这些都很好,但是它并不能帮助我在不创建无参构造函数的情况下强制插入。我能做到这一点吗……没有插入、检查、删除、插入? - Mihai Danila

55

如果您想插入一个新元素,请使用insertinsert不会覆盖现有的元素,您可以验证之前不存在该元素:

if ( !myMap.insert( std::make_pair( key, value ) ).second ) {
    //  Element already present...
}

如果您想覆盖可能已经存在的元素,请使用[]:

myMap[ key ] = value;
assert( myMap.find( key )->second == value ); // post-condition

这个表单将覆盖任何现有的条目。


13

引用:

由于映射容器不允许重复的关键字值,插入操作会检查每个要插入的元素是否已经存在于容器中具有相同关键字值的另一个元素,如果存在,则该元素不会被插入,并且它的映射值也不会以任何方式改变。

因此,如果关键字已经存在,插入操作将不会改变值,而[]运算符会改变。

编辑:

这让我想起另一个最近的问题——为什么要使用at()而不是[]运算符从向量中检索值。显然,如果下标超出范围,at()将抛出异常,而[]运算符则不会。在这些情况下,最好查阅函数文档,因为它们将提供所有细节。但一般来说,没有(或者至少不应该有)两个执行完全相同操作的函数/运算符。

我猜测,在内部,insert()首先检查条目,然后自己使用[]运算符


实际上,在GNU libstdc++中恰恰相反:operator[]查找现有元素,如果未找到,则使用默认构造的值调用insert - vaclav.blazek

11

map[key] = value是提供更简便的语法。更易于阅读和书写。

你需要具有默认构造函数的原因是,在赋值之前会评估map[key]。如果key不存在于map中,则创建一个新对象(使用默认构造函数),并从operator[]返回对其的引用。


2
它不仅仅是语法。当map已经包含了一个键的值时,map[key] = value;map.insert({key,value});是不同的。 - 463035818_is_not_a_number

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接