插入地图的首选/惯用方式是什么?

问题描述 投票:88回答:9

我已经确定了将元素插入到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));

其中哪些是首选/惯用的方式? (还有我没想到的另一种方法吗?)

c++ stl insert stdmap std-pair
9个回答
79
投票

首先,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可能是可行的方法。

  • 83
    投票
    从C ++ 11开始,您有两个主要的附加选项。首先,可以将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方法通常不是那样使用的。

  • 9
    投票
    第一个版本:

    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_typetypedefstd::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());


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

    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%的时间。


    2
    投票
    简而言之,[]运算符更新值的效率更高,因为它涉及到调用值类型的默认构造函数,然后为其分配新值,而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));
        }
    }
    

    您可能决定选择一种不使用通用编程的版本,但是重点是我发现这种范例(区分“添加”和“更新”)非常有用。


    1
    投票

    如果要用键0覆盖元素,则>

    function[0] = 42;
    

    1
    投票

    [如果要在std :: map中插入元素-请使用insert()函数,并且要查找元素(按键)并为其分配一些元素-请使用operator []。


    1
    投票

    我只是稍微改变了一下问题(字符串映射)以显示插入的另一个兴趣:


    0
    投票

    由于C++17 std::map提供了两种新的插入方法:std::mapinsert_or_assign(),同样在insert_or_assign()中提到。

    © www.soinside.com 2019 - 2024. All rights reserved.