我已经确定了将元素插入到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
成员函数在功能上不等效:
operator[]
将搜索作为键,如果未找到,则插入一个默认构造的值,并返回给您分配值的引用。显然,如果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
依赖于模板参数推导,并且可能(在这种情况下为[[will)产生的类型与映射的实际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
成员函数期望的参数类型。operator[]
,除非在默认构造和分配mapped_type
时没有额外的花费,并且我不在乎确定是否有新的密钥已有效插入。使用insert
时,构造value_type
可能是可行的方法。insert()
与列表初始化语法一起使用:function.insert({0, 42});
在功能上等同于
function.insert(std::map<int, int>::value_type(0, 42));
但更简洁易读。正如其他答案所指出的,与其他形式相比,它具有多个优点:
operator[]
方法要求映射类型是可分配的,但并非总是如此。operator[]
方法可以覆盖现有元素,并且使您无法判断是否发生了这种情况。insert
的其他形式涉及隐式类型转换,这可能会降低您的代码速度。unique_ptr
值的地图。该问题已在标准中修复,但该修复可能尚未达到您的标准库实现。第二,您可以使用emplace()
方法:
function.emplace(0, 42);
这比insert()
的任何形式都更简洁,可以与unique_ptr
这样的仅移动类型一起使用,并且从理论上讲可能稍微更有效(尽管不错的编译器应该优化差异)。唯一的主要缺点是,它可能会使您的读者有些惊讶,因为emplace
方法通常不是那样使用的。
function[0] = 42; // version 1
可以或可以不将值42插入地图。如果键0
存在,则它将为该键分配42,覆盖该键具有的任何值。否则,它将插入键/值对。插入功能:
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
已存在于地图中,则不要执行任何操作。如果键不存在,它将插入键/值对。这三个插入函数几乎相同。
std::map<int, int>::value_type
是typedef
的std::pair<const int, int>
,std::make_pair()
显然是通过模板演绎魔术产生的std::pair<>
。但是,最终版本应该与版本2、3和4相同。我会用哪个?我个人更喜欢版本1。简洁而自然。当然,如果不需要覆盖行为,那么我会选择版本4,因为它比版本2和3需要更少的输入。我不知道是否有一种
deacto插入键/的方式。值对成
通过其构造函数之一将值插入地图的另一种方法: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)));
2290毫秒,2037毫秒,2046毫秒
map_W_3[it] = Widget(2.0);
2592 ms,2278 ms,2296 ms
map_W_0.insert(std::map<int,Widget>::value_type(it,Widget(2.0)));
2234毫秒,2031毫秒,2027毫秒因此,不同插入版本之间的结果可以忽略(尽管没有执行假设检验!]
由于使用Widget的默认构造函数进行初始化,因此本示例中的
map_W_3[it] = Widget(2.0);
版本花费了大约10-15%的时间。
[]
运算符更新值的效率更高,因为它涉及到调用值类型的默认构造函数,然后为其分配新值,而insert()
运算符对于添加值更有效。 Scott Meyers撰写的
有效STL:有效STL:改善标准模板库使用的50种特定方法]中引用的代码段],第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)); } }
您可能决定选择一种不使用通用编程的版本,但是重点是我发现这种范例(区分“添加”和“更新”)非常有用。
如果要用键0覆盖元素,则>
function[0] = 42;
[如果要在std :: map中插入元素-请使用insert()函数,并且要查找元素(按键)并为其分配一些元素-请使用operator []。
我只是稍微改变了一下问题(字符串映射)以显示插入的另一个兴趣:
由于C++17 std::map
提供了两种新的插入方法:std::map
和insert_or_assign()
,同样在insert_or_assign()
中提到。