为什么C++ std::map::operator[] 不使用就地新?
Why doesn't C++ std::map::operator[] use inplace new?
如果您将C++std::map(和其他容器)与值类型一起使用,您会注意到插入到map中会调用元素类型的析构函数。这是因为C++规范要求运算符[]的实现等效于此:
(*((std::map<>::insert(std::make_pair(x, T()))).first)).second
它调用类型的默认构造函数来构建该对。然后将该临时值复制到映射中,然后进行销毁。这一点的确认可以在这篇stackoverflow帖子和这里的codeguru上找到。
我觉得奇怪的是,这可以在不需要临时变量的情况下实现,并且仍然是等效的。C++有一个特性叫做"就地新"。std::map和其他容器可以为对象分配空白空间,然后在分配的空间上显式调用元素的默认构造函数。
我的问题是:为什么我看到的std::map的实现都没有使用inplace new来优化此操作?在我看来,这将大大提高这种低级别操作的性能。但是很多人都研究过STL代码库,所以我认为这样做一定有原因。
通常,根据较低级别的操作来指定像[]
这样的较高级别操作是个好主意。
在C++11之前,如果不使用insert
,使用[]
将很难做到这一点。
在C++11中,为std::pair
添加std::map<?>::emplace
和类似的东西使我们能够避免这个问题。如果你重新定义了它,使用这样的就地构造,那么额外的(希望被忽略的)对象创建就会消失。
我想不出有什么理由不这样做。我鼓励你提出标准化。
为了演示无拷贝插入std::map
,我们可以执行以下操作:
#include <map>
#include <iostream>
struct no_copy_type {
no_copy_type(no_copy_type const&)=delete;
no_copy_type(double) {}
~no_copy_type() { std::cout << "destroyedn"; }
};
int main() {
std::map< int, no_copy_type > m;
m.emplace(
std::piecewise_construct,
std::forward_as_tuple(1),
std::forward_as_tuple(3.14)
);
std::cout << "destroy happens next:n";
}
实际示例--正如您所看到的,没有生成临时的。
所以如果我们更换
(*((std::map<>::insert(std::make_pair(x, T()))).first)).second
带有
(*
(
(
std::map<>::emplace(
std::piecewise_construct,
std::forward_as_tuple(std::forward<X>(x)),
std::forward_as_tuple()
)
).first
).second
不会创建临时的(添加空白,这样我就可以跟踪()
s)。
首先,如果未找到请求的<key>
,则std::map
的operator[<key>]
仅等效于插入操作。在这种情况下,只需要对密钥的引用,并且只需要对生成的存储值的引用。
其次,当插入新元素时,无法知道是否会执行复制操作。您可能有map[_k] = _v;
,也可能有_v = map[_k];
。当然,后者的要求与作业外的要求相同,即map[_k].method_call();
,但不使用复制构造函数(没有可从中构造的源)。关于插入,上述所有操作都要求调用value_type
的默认构造函数并为其分配空间。即使我们在编写operator[]
时可以知道我们处于赋值用例中,但由于操作顺序的原因,我们不能使用"inplace new"。必须首先调用value_type
构造函数,然后调用value_type::operator=
,这就需要调用复制构造函数。
不过想法不错。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- std::原子加载和存储都需要吗
- 将对象移动到std::shared_ptr
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 使用一个考虑到std::map中键值的滚动或换行的键
- 如何从 std::atomic 中提取指针 T<T>?
- 为什么 std::unique 不调用 std::sort?
- 使用std::函数映射对象方法
- 可组合的lambda/std::函数与std::可选