插入地图的首选/惯用方式

我已经确定了插入std::map四种不同的方法:

 std::map<int, int> function; function[0] = 42; function.insert(std::map<int, int>::value_type(0, 42)); function.insert(std::pair<int, int>(0, 42)); function.insert(std::make_pair(0, 42)); 

哪一种是首选/惯用的方式? (还有没有想过的另一种方式?)

首先, operator[]insert成员函数在function上并不等同:

  • operator[]search该键,如果找不到,则插入一个默认的构造值,并返回一个给其赋值的引用。 显然,如果mapped_type可以受益于直接初始化,而不是缺省的构造和赋值,这可能是低效的。 这种方法也使得不可能确定插入是否确实发生,或者是否只覆盖了先前插入的键的值
  • 如果键已经存在于映射中,那么insert成员函数将不起作用,虽然它经常被遗忘,但是返回一个std::pair<iterator, bool> ,这可能是有趣的(最显着的是确定插入是否有实际的已经完成)。

从列出的所有insert可能性中,所有三个几乎是等价的。 提醒一下,让我们看看标准中的insert签名:

 typedef pair<const Key, T> value_type; /* ... */ pair<iterator, bool> insert(const value_type& x); 

那三个电话又是怎样的呢?

  • std::make_pair依赖于模板参数的推导,并可以(在这种情况下) 产生一些不同于地图的实际value_type的东西,这将需要额外的调用std::pair模板的构造函数,以转换为value_type (即:将const添加到first_type
  • std::pair<int, int>也需要对std::pair的模板构造函数进行额外的调用,以便将参数转换为value_type (即:将const添加到first_type
  • std::map<int, int>::value_type完全没有地方值得怀疑,因为它直接是insert成员函数所期望的参数types。

最后,当目标是插入时,我会避免使用operator[] ,除非默认情况下没有额外的成本 – 构造和分配mapped_type ,并且我不关心确定新密钥是否已经有效插入。 当使用insert ,构build一个value_type可能是一种方法。

从C ++ 11开始,你有两个主要的附加选项。 首先,你可以使用insert()和list初始化语法:

 function.insert({0, 42}); 

这在function上等同于

 function.insert(std::map<int, int>::value_type(0, 42)); 

但更简洁可读。 正如其他答案所指出的那样,这与其他forms相比有几个优点:

  • operator[]方法要求映射types是可分配的,但情况并非总是如此。
  • operator[]方法可以覆盖现有元素,并且无法分辨是否发生了这种情况。
  • 您列出的insert的其他forms涉及隐式types转换,这可能会减慢您的代码。

主要的缺点是,这种forms用来要求键和值是可复制的,所以它不适用于例如具有unique_ptr值的映射。 标准中已经修复了这个问题,但是修正可能还没有达到你的标准库实现。

其次,你可以使用emplace()方法:

 function.emplace(0, 42); 

这比insert()任何forms都要简洁,对于像unique_ptr这样的只移动types来说工作得很好,理论上可能稍微有效一些(尽pipe体面的编译器应该优化差异)。 唯一的缺点是可能会让读者感到惊讶,因为emplace方法通常不是这样使用的。

第一个版本:

 function[0] = 42; // version 1 

可能会或可能不会将值42插入到地图中。 如果密钥0存在,那么它将分配42到该密钥,覆盖该密钥所具有的任何值。 否则,它插入键/值对。

插入function:

 function.insert(std::map<int, int>::value_type(0, 42)); // version 2 function.insert(std::pair<int, int>(0, 42)); // version 3 function.insert(std::make_pair(0, 42)); // version 4 

另一方面,如果键0已经存在于地图中,则不做任何事情。 如果密钥不存在,则插入密钥/值对。

三个插入function几乎完全相同。 std::map<int, int>::value_typestd::pair<const int, int>std::make_pair()显然通过模板演绎魔术产生std::pair<> 。 然而,最终的结果应该是相同的版本2,3和4。

我会用哪一个? 我个人比较喜欢版本1; 它简洁而“自然”。 当然,如果它的覆盖行为是不需要的,那么我宁愿使用版本4,因为它比版本2和3需要更less的input。我不知道是否有一个事实上的方法将键/值对插入到std::map

另一种通过其构造函数将值插入到地图中的方法:

 std::map<int, int> quadratic_func; quadratic_func[0] = 0; quadratic_func[1] = 1; quadratic_func[2] = 4; quadratic_func[3] = 9; std::map<int, int> my_func(quadratic_func.begin(), quadratic_func.end()); 

我已经在上述版本之间进行了一些比较:

 function[0] = 42; function.insert(std::map<int, int>::value_type(0, 42)); function.insert(std::pair<int, int>(0, 42)); function.insert(std::make_pair(0, 42)); 

发现插入版本之间的时间差异很小。

 #include <map> #include <vector> #include <boost/date_time/posix_time/posix_time.hpp> using namespace boost::posix_time; class Widget { public: Widget() { m_vec.resize(100); for(unsigned long it = 0; it < 100;it++) { m_vec[it] = 1.0; } } Widget(double el) { m_vec.resize(100); for(unsigned long it = 0; it < 100;it++) { m_vec[it] = el; } } private: std::vector<double> m_vec; }; int main(int argc, char* argv[]) { std::map<int,Widget> map_W; ptime t1 = boost::posix_time::microsec_clock::local_time(); for(int it = 0; it < 10000;it++) { map_W.insert(std::pair<int,Widget>(it,Widget(2.0))); } ptime t2 = boost::posix_time::microsec_clock::local_time(); time_duration diff = t2 - t1; std::cout << diff.total_milliseconds() << std::endl; std::map<int,Widget> map_W_2; ptime t1_2 = boost::posix_time::microsec_clock::local_time(); for(int it = 0; it < 10000;it++) { map_W_2.insert(std::make_pair(it,Widget(2.0))); } ptime t2_2 = boost::posix_time::microsec_clock::local_time(); time_duration diff_2 = t2_2 - t1_2; std::cout << diff_2.total_milliseconds() << std::endl; std::map<int,Widget> map_W_3; ptime t1_3 = boost::posix_time::microsec_clock::local_time(); for(int it = 0; it < 10000;it++) { map_W_3[it] = Widget(2.0); } ptime t2_3 = boost::posix_time::microsec_clock::local_time(); time_duration diff_3 = t2_3 - t1_3; std::cout << diff_3.total_milliseconds() << std::endl; std::map<int,Widget> map_W_0; ptime t1_0 = boost::posix_time::microsec_clock::local_time(); for(int it = 0; it < 10000;it++) { map_W_0.insert(std::map<int,Widget>::value_type(it,Widget(2.0))); } ptime t2_0 = boost::posix_time::microsec_clock::local_time(); time_duration diff_0 = t2_0 - t1_0; std::cout << diff_0.total_milliseconds() << std::endl; system("pause"); } 

这分别给出了版本(我跑了3次文件,因此连续3个时间差异):

 map_W.insert(std::pair<int,Widget>(it,Widget(2.0))); 

2198毫秒,2078毫秒,2072毫秒

 map_W_2.insert(std::make_pair(it,Widget(2.0))); 

2290ms,2037ms,2046ms

  map_W_3[it] = Widget(2.0); 

2592毫秒,2278毫秒,2296毫秒

  map_W_0.insert(std::map<int,Widget>::value_type(it,Widget(2.0))); 

2234ms,2031ms,2027ms

因此,不同插入版本之间的结果可以忽略不计(虽然没有进行假设检验)!

map_W_3[it] = Widget(2.0); 由于使用Widget的默认构造函数进行初始化,因此该示例需要大约10-15%的时间。

如果你想用键0覆盖元素

 function[0] = 42; 

除此以外:

 function.insert(std::make_pair(0, 42)); 

如果要在std :: map中插入元素 – 使用insert()函数,并且如果要查找元素(通过键)并为其指定一些元素,请使用运算符[]。

为了简化插入使用boost :: assign库,像这样:

 using namespace boost::assign; // For inserting one element: insert( function )( 0, 41 ); // For inserting several elements: insert( function )( 0, 41 )( 0, 42 )( 0, 43 ); 

简而言之, []运算符更新值的效率更高,因为它涉及调用值types的默认构造函数,然后为其赋值一个新值,而insert()则更有效地添加值。

斯科特·迈耶斯(Scott Meyers)提出的“ Effective STL:50种改善标准模板库使用的具体方法” (Item 24)中的引用片段可能有所帮助。

 template<typename MapType, typename KeyArgType, typename ValueArgType> typename MapType::iterator insertKeyAndValue(MapType& m, const KeyArgType&k, const ValueArgType& v) { typename MapType::iterator lb = m.lower_bound(k); if (lb != m.end() && !(m.key_comp()(k, lb->first))) { lb->second = v; return lb; } else { typedef typename MapType::value_type MVT; return m.insert(lb, MVT(k, v)); } } 

你可能决定select一个通用编程的免费版本,关键是我发现这个范例(区分'add'和'update')非常有用。

我只是稍微改变一点问题(string图)来显示插入的另一个兴趣:

 std::map<int, std::string> rancking; rancking[0] = 42; // << some compilers [gcc] show no error rancking.insert(std::pair<int, std::string>(0, 42));// always a compile error 

编译器在“rancking [1] = 42;”上显示没有错误的事实 会产生毁灭性的影响!