在std :: map中最佳创建新的空元素

时间:2018-09-15 10:49:26

标签: c++ c++11 stdmap std-pair

要插入新元素std::map,需要存在std::pair对象。 我没有找到一种替代方法,无需构建这样的std::map对象就可以向std::pair中添加新元素。

但是,假设std::map值很重,会消耗大量资源。将此类元素插入std::map会限制一个不必要的副本。在这种情况下 我有一些对象X需要直接复制到std::map中,std作者限制我分两步执行此复制操作:

  1. 复制X-> std::pair
  2. 复制对obj-> std::map插入位置。

如何消除这些不需要的副本?实现Move构造函数无法解决我的问题-副本仍然存在。

我怀疑std::map中的某个方法会插入带有一些键但没有值的新空元素,而不会为该值调用任何构造函数。然后,我可以按照自己的方式移动值。 但是我在std::map中找不到这种方法!

2 个答案:

答案 0 :(得分:3)

答案 1 :(得分:2)

  

std::map 是消耗大量资源的沉重对象。将此类元素插入std::map会限制一个不必要的操作   复制。如何消除这些不需要的副本?

您对此是正确的,std::map::emplace是我们现在可用的唯一解决方案。以下是有关cppreference.comstd::map::emplace的使用的小解释:

  

谨慎使用emplace可以在构建新元素的同时   避免不必要的复制或移动操作。的构造函数   恰好是来调用新元素(即std::pair<const Key, T>)   与提供给Emplace的参数相同,通过   std::forward<Args>(args)....

这意味着可以在地图插入时就地构建一个类实例(即,代替构造和复制),方法是通过{{1 }},当您在类中为承包商提供完全相同的参数时。然后,您需要将std::map::emplace std::piecewise_construct std::forward_as_tuple 一起使用(假设该类包含多个成员)

std::map::emplace

为演示上述情况,我制作了一个小的示例代码,其中将myMap.emplace( std::piecewise_construct, std::forward_as_tuple(/*key of map*/), std::forward_as_tuple(/*all the members which you want to constrct in place*/) ); 的成员构建到位,而无需调用任何特殊的成员函数。为了确保这一点,我禁用了ClassAdefaultcopy构造函数。

SEE LIVE HERE

move

输出:

#include <iostream>
#include <map>
#include <tuple>

class ClassA
{
    int _val;
    std::string _str;
public:
    explicit ClassA(const int val, const std::string& str) : _val(val), _str(str)
    {
        std::cout << "A class: C'tor called...!\n";
    }
    // disable the following
    ClassA() = delete;
    ClassA(const ClassA&) = delete;
    ClassA& operator=(const ClassA&) = delete;
    ClassA(ClassA&&) = delete;
    ClassA& operator=(ClassA&&) = delete;

};

class ClassB
{
    ClassA _aObj;
public:
    explicit ClassB(const int val, const std::string& str) : _aObj(val, str)
    {
        std::cout << "B class: C'tor called...!\n";
    }
    // disable the following
    ClassB() = delete;
    ClassB(const ClassB&) = delete;
    ClassB& operator=(const ClassB&) = delete;
    ClassB(ClassB&&) = delete;
    ClassB& operator=(ClassB&&) = delete;
};

int main()
{
    std::map<int, ClassB> myMap;
    myMap.emplace(
        std::piecewise_construct,
        std::forward_as_tuple(1),
        std::forward_as_tuple(1, "some string")
    );
    return 0;
}

更新:另一方面,

  

即使,也可以构造该元素   容器中已经有一个具有键的元素,其中   否则新构造的元素将立即被销毁。

这意味着您对以下方面的期望(或假设):

我怀疑A class: C'tor called...! B class: C'tor called...! 中的某些方法会插入带有某些键无值的新的空元素,而无需为该值调用任何构造函数。然后我可以按照自己的方式将值移动到该位置。

通过上述std::map方法,

不可能实现。正如@aschepler在评论中指出的那样,通过使用C ++ 17功能 std::optional ,您可以使地图有时带有(可选)键,而没有值。

为此,您需要按如下所示将值设置为可选值,并且当然编译器版本支持C ++ 17或更高版本。

std::map::emplace

现在,您可以随时在代码中构造一个对象,过一会儿,您可以将其移动到适当的键值(即,条目)。最后但并非最不重要的一点是,不要忘记提供default move-c'ntors

SEE SAMPLE CODE HERE

std::map<Key, std::optional<Value>> myMap;

输出:

#include <optional>

class ClassA {
    /* same as before*/
public:
    // disable the copy c'tor
    // enable move c'ntors
    ClassA(ClassA&&) = default;
    ClassA& operator=(ClassA&&) = default;

};

class ClassB {
    /* same as before*/
public:
    // disable the copy c'tor
    // enable move c'ntors
    ClassB(ClassB&&) = default;
    ClassB& operator=(ClassB&&) = default;
};

int main() {
    std::map<int, std::optional<ClassB>> myMap;
    // created without calling any constructors
    myMap.emplace(1, std::nullopt);
    //later in the code
    ClassB bObj{1, "JeJo"};
    // again after..... move it to the required key-value
    myMap[1] = std::move(bObj);
    return 0;
}