c++ - 在 std::map 中优化创建新的空元素
问题描述
插入新元素std::map
需要存在std::pair
对象。我没有找到在std::map
不构造此类std::pair
对象的情况下添加新元素的替代方法。
但是假设std::map
值是一个消耗大量资源的重物。将这样的元素插入到std::map
约束中一个不必要的副本。如果我有一些对象 X 需要直接复制到std::map
中,std 作者会限制我分两步执行此复制:
- 复制 X->
std::pair
- 复制对 obj ->
std::map
插入位置。
如何消除这些不需要的副本?实现 Move 构造函数并不能解决我的问题 - 副本仍然存在。
我怀疑某些方法std::map
会插入一个带有一些键但没有值的新空元素,而无需为该值调用任何构造函数。然后我可以将值移动到我自己的方式。但我没有找到这样的方法std::map
!
解决方案
该
std::map
值是一个消耗大量资源的重物。将这样的元素插入到std::map
约束中一个不必要的副本。如何消除这些不需要的副本?
您对此是正确的,并且std::map::emplace
是我们现在可用的唯一解决方案。下面给出了关于使用std::map::emplace
from cppreference.com的小说明 :
谨慎使用
emplace
允许构造新元素,同时避免不必要的复制或移动操作。新元素 (iestd::pair<const Key, T>
) 的构造函数被调用,其参数与提供给 emplace的参数完全相同 ,通过转发std::forward<Args>(args)....
这意味着当您在类中提供具有完全相同参数的承包商时,可以在插入地图时(即,而不是构造和复制)就地构造 a 类实例。然后,您需要与and一起使用(假设该类包含多个成员)。std::map::emplace
std::map::emplace
std::piecewise_construct
std::forward_as_tuple
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*/)
);
为了演示上述情况,我制作了一个小示例代码,其中的成员ClassA
将在适当的位置构造,而无需调用任何特殊的成员函数。为了确保这一点,我禁用了default
,copy
和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...!
更新:另一方面,
即使容器 中已经有一个带有key的元素,也可以构造该元素,在这种情况下,新构造的元素将立即被销毁。
这意味着,您的期望(或假设):
“ 我怀疑某些方法std::map
会插入一个新的空元素,其中包含一些键但没有值,而不为该值调用任何构造函数。然后我可以将值移动到我自己的方式。 ”
通过上述方式是不可能std::map::emplace
实现的。正如@aschepler 在评论中指出的那样,您可以通过使用 C++17 特性来获得带有有时(可选)键且没有值的映射std::optional
。
为此,您需要将这些值设为可选,如下所示,当然编译器版本支持 C++17 或更高版本。
std::map<Key, std::optional<Value>> myMap;
现在您可以随时在代码中构造一个对象,一段时间后,您可以将其移动到适当的键值(即条目)。最后但同样重要的是,不要忘记提供默认的 move-c'ntors
#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;
}
输出:
A class: C'tor called...!
B class: C'tor called...!
推荐阅读
- postgresql - 创建一个创建新序列的过程/函数
- java - Guice 和通用工厂
- java - 当我不知道组是多少时,如何将字符串存储为组?(在Java中)
- ios - 当我上下滚动 UICollectionView 时,视图高度约束设置不正确
- angular - WPF 的 ContentPresenter 的 Angular 等价物是什么?
- android - 如何为 Room 注入我的应用程序类
- php - 我想从这个 JSON URL 回显 IMDB ID、标题、质量和嵌入 URL
- c# - 如何在 C# 和 ASP.NET MVC 中从视图访问数据到模型
- python - 当数据框列中的值为 x 时,使用 Jupyter Notebooks 和 Python 计算另一个列值为 y 的次数?
- matlab - 如何在 MATLAB 中从国际标准大气模型绘制多个图形?