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

Kel*_*ang 12 c++ move-semantics

正如cppreference.com所说,

地图通常以红黑树的形式实现。

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

How*_*ant 13

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

移出的地图必须处于有效状态。 即,从移出时map必须有一个结束节点以指向何时end()被调用。在进行移动构造之前map,您将要从中移动的末端节点。在移动构造之后,必须存在两个末端节点:一个在新节点中map,一个在移动自`地图中。

如果末端节点进入堆,这意味着move构造函数不会转移末端节点的所有权,因此必须为新的`map分配一个新的末端节点。或确实转移了末端节点,但随后必须分配一个新节点以留在移出的源中。

如果将末端节点嵌入到map数据结构本身中,则永远不需要在堆上分配它。map构造后,它会自动“分配在堆栈上” 。

如果需要的话,允许实现将其map构造为move构造函数noexcept,而无需这样做。

这是我几年前在实现中对默认构造函数,容器的move构造函数和move赋值运算符的noexcept状态进行的调查。此调查假定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 constructible\n";
    else
        std::cout << name << " is NOT noexcept default constructible\n";
    if (is_nothrow_move_constructible<C>::value)
        std::cout << name << " is noexcept move constructible\n";
    else
        std::cout << name << " is NOT noexcept move constructible\n";
    if (is_nothrow_move_assignable<C>::value)
        std::cout << name << " is noexcept move assignable\n\n";
    else
        std::cout << name << " is NOT noexcept move assignable\n\n";
}

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>>();
}
Run Code Online (Sandbox Code Playgroud)

这个答案从何"type_name.h"而来。