如何优化std :: map insert()函数?

时间:2017-06-20 18:46:12

标签: c++ windows visual-studio dictionary std

解释我尝试完成的内容的最佳方法是使用此示例(使用Visual Studio 2008 SP1编译):

struct ELEMENT1{
    //Its members

    ELEMENT1()
    {
        //Constructor code
    }
    ~ELEMENT1()
    {
        //Destructor code
    }
};


std::map<std::wstring, ELEMENT1> map;

std::pair<std::map<std::wstring, ELEMENT1>::iterator, bool> resIns;
ELEMENT1 element;
std::wstring strKey;

for(size_t i = 0; i < numberRepetitions; i++)
{
    //Do processing
    //...

    //set 'strKey'

    //Insert new element into the map first
    resIns = map.insert(std::pair<std::wstring, ELEMENT1>(strKey, element));    //This line calls ELEMENT1 constructor & destructor twice

    //Then fill out the data
    fill_in_data(resIns.first->second);
}


BOOL fill_in_data(ELEMENT1& outInfo)
{
    //Fill in 'outInfo' -- MUST be in its own function
    //...
}

我的目标是优化此代码,因此我执行了以下操作:

  • 在循环之外移动ELEMENT1 element构造/销毁。

  • 我将element插入map,然后尝试使用指向插入元素的指针填充它,而不是构造新元素,然后将其填满,然后将其复制到地图中,然后将其销毁。 (至少那是计划。)

但是当我为Release构建编译它并检查汇编代码时,我可以看到带有map.insert()函数的C ++行调用ELEMENT1构造函数两次!然后两次析构。因此,以下机器代码仅适用于map.insert()行:

enter image description here

所以我显然没有在这里看到什么。

有人可以建议在已编译的代码中进行的操作吗?如果有可能优化它?

3 个答案:

答案 0 :(得分:2)

您有2个构造函数调用的原因是因为您传递给insert的内容与它所需的内容不匹配。 std::map::insert需要const value_type&value_type需要地图

std::pair<const key_type, element_type>
          ^^^^^ this is important

因此,由于它们不匹配,因此在使用

时构造一个元素
std::pair<std::wstring, ELEMENT1>(strKey, element)

然后编译器调用复制构造函数将其转换为

std::pair<const std::wstring, ELEMENT1>

快速解决方法是将代码更改为

std::pair<const std::wstring, ELEMENT1>(strKey, element)

这会留下一个构造和破坏的临时建筑。您也可以zett42中的their answer建议,以避免完全创建临时。

答案 1 :(得分:1)

resIns = map.insert(std::pair<std::wstring, ELEMENT1>(strKey, element));

您正在构建一个临时std::pair,其成员secondELEMENT1。这会导致调用ELEMENT1复制构造函数

第二次调用ELEMENT1的复制构造函数是std::map::insert()在地图中创建一个新元素,该元素将由临时std::pair初始化。

您可以使用std::map::operator[]来避免由临时引起的重复构造函数调用:

ELEMENT1& resIns = map[ strKey ];

fill_in_data( resIns );

如果地图中尚不存在strKey,则会在地图中直接构造ELEMENT1,并返回对新对象的引用。构造函数将被调用一次。

如果地图中已存在strKey,则将返回对现有对象的引用。

答案 2 :(得分:-1)

你应该使用emplace来避免在临时对象上创建:

resIns = map.emplace
(
    ::std::piecewise_construct
,   ::std::forward_as_tuple(strKey)
,   ::std::forward_as_tuple()
);

切换到较新的VS版本的一个很好的理由。