【问题标题】:Why is std::map's move constructor not noexcept?为什么std::map的移动构造函数不是noexcept?
【发布时间】:2019-12-09 11:29:20
【问题描述】:

正如 cppreference.com 所说,

地图通常被实现为红黑树。

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

【问题讨论】:

标签: c++ move-semantics


【解决方案1】:

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

A moved-from map must be in a valid state. 即当end() 被调用时,一个被移动的map 必须有一个结束节点指向。在移动构造之前,map 中存在一个您将要从中移动的端节点。在移动构造之后,必须存在两个端节点:一个在新的map 中,一个在移动后的`map 中。

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

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

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

这是我几年前实现的survey of the noexcept-state of the default constructor, move constructor and move assignment operator of the containers。本调查假设每个容器都使用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>>();
}

"type_name.h" 来自this answer

【讨论】:

  • 查看调查,似乎 MSVC 的 std::map 实现不是可移动的。我很好奇 MSVC 的实现有什么特别之处,std::map 不能被 noexcept 移动?
  • 对于move构造函数,他们将结束节点放在堆上,因此在move构造过程中总是需要分配一个结束节点。
  • 如果结束节点在堆上,为什么在移动过程中不能复制指向结束节点的指针?
  • A moved-from map must be in a valid state. 即当end() 被调用时,从map 移出的必须有一个结束节点指向。在移动构建之前,存在一个末端节点。移动构建后必须存在两个端节点。如果设计是端节点在堆上,那么必须分配一个新的。如果设计是末端节点嵌入map,那么末端节点“分配”是“在堆栈上”。
  • 谢谢。这就是我正在寻找的答案。您会编辑您的答案以包含此评论吗?
猜你喜欢
  • 2020-05-08
  • 2023-04-08
  • 2014-12-01
  • 2021-04-07
  • 2018-10-06
  • 2023-03-16
  • 1970-01-01
  • 2012-03-04
  • 2014-05-07
相关资源
最近更新 更多