为什么 std::map 的移动构造函数不是 noexcept?

Why is std::map's move constructor not noexcept?

本文关键字:noexcept 构造函数 移动 std map 为什么      更新时间:2023-10-16

正如 cppreference.com 所说,

地图通常实现为红黑树。

因此,移动std::map只是将指针移动到根node+ 其他信息(如大小)。为什么std::map的移动构造函数没有标记为noexcept

这是因为我无法将所有实现者都置于可以放入map的无资源状态。 例如,实现需要有一个要指向的终端节点,即使在默认构造状态下也是如此。 允许(但不是必需)实现将该终端节点放在堆上。

移自映射必须处于有效状态。 即,当调用end()时,移自map必须有一个指向的结束节点。在移动构造之前,map中存在一个要从中移动的终端节点。在移动构造之后,必须存在两个终端节点:一个在新map中,另一个在移自"地图"中。

如果终端节点进入堆,这意味着移动构造函数要么不转移结束节点的所有权,因此必须为新的"映射"分配新的结束节点。或者确实转移了终端节点,但随后必须分配一个新节点以保留在移自源中。

如果终端节点嵌入在map数据结构本身中,则永远不需要在堆上分配它。 随着map的构建,它会自动"在堆栈上分配"。

允许实现使map移动构造函数noexcept如果他们愿意,他们只是不需要这样做。

以下是我几年前进行的实现中容器的默认构造函数、移动构造函数和移动赋值运算符的 noexexcept 状态的调查。 此调查假定每个容器std::allocator。 我只是抽查了map,结果没有改变。

如果您想自己运行此调查,以下是代码:

#include "type_name.h"
#include <iostream>
#include <type_traits>
#include <deque>
#include <forward_list>
#include <list>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
template <class C>
void
report()
{
using namespace std;
const auto name = type_name<C>();
if (is_nothrow_default_constructible<C>::value)
std::cout << name << " is noexcept default constructiblen";
else
std::cout << name << " is NOT noexcept default constructiblen";
if (is_nothrow_move_constructible<C>::value)
std::cout << name << " is noexcept move constructiblen";
else
std::cout << name << " is NOT noexcept move constructiblen";
if (is_nothrow_move_assignable<C>::value)
std::cout << name << " is noexcept move assignablenn";
else
std::cout << name << " is NOT noexcept move assignablenn";
}
int
main()
{
using namespace std;
report<deque<int>>();
report<forward_list<int>>();
report<list<int>>();
report<vector<int>>();
report<string>();
report<map<int, int>>();
report<set<int>>();
report<unordered_map<int, int>>();
report<unordered_set<int>>();
}

"type_name.h"从这个答案中来。

相关文章: