【问题标题】:Why does std::insert require CopyConstructibility?为什么 std::insert 需要 CopyConstructibility?
【发布时间】:2018-07-25 22:36:13
【问题描述】:

为什么这段代码不起作用? std::map 的插入方法的文档中没有关于 CopyConstructibility 的内容:http://en.cppreference.com/w/cpp/container/map/insert

我可以构造 std::pair ,第二个元素是我的 non_copyable 类,但我不能将它插入到 map 中,即使 insert 方法的第一个重载采用 const value_type&,所以这应该可以正常工作。

有人能帮我理解为什么 std::map 需要这个 CopyConstructibility 吗?

#include <utility>
#include <map>

struct non_copyable {
    non_copyable(){}

private:
    non_copyable(const non_copyable&);
};

int main() {
    std::pair<int, non_copyable> p; // this works!

    std::map<int, non_copyable> m;
    m.insert(std::pair<int, non_copyable>()); // this does NOT work
}

示例:https://wandbox.org/permlink/r3H6CqehfqnHPXDL

【问题讨论】:

  • 因为它制作了一对的副本
  • 改用emplace。这就是它存在的目的。
  • 显式定义的复制构造函数禁用了移动构造函数的隐式定义。如果可用,您需要non_copyable(non_copyable&amp;&amp;) = default; 选择正确的插入重载(除非我弄错了,否则它在 c++17 中)。
  • emplace 在 c++11 之前不可用。 insert 有不止一种形式,在 c++11 之前只有一种可用,它正在复制变量(它使用 const-lvalue 引用 const T &amp;)。但是在新标准中,您可以使用emplace,或者您可以使用insert 本身与“移动”变体(采用T &amp;&amp;),但这需要您定义“移动”构造函数。 non_copyable(non_copyable&amp;&amp;) = default; 尝试阅读“引用类型”、“复制/移动构造函数”、“复制/移动分配”

标签: c++


【解决方案1】:

...即使插入方法的第一个重载采用 const value_type&,所以这应该可以正常工作。

不,实际上在这种情况下,正确的重载解决方案候选:

m.insert(std::pair<int, non_copyable>());

这是重载的成员函数吗:

template< class P >
std::pair<iterator,bool> insert( P&& value );

现在this page 描述了为什么这不起作用:

...仅在std::is_constructible&lt;value_type, P&amp;&amp;&gt;::value == true 时才参与重载决议。

您的non_copyable 不可构造:

using M = std::map<int, non_copyable>;
using P = std::pair<int, non_copyable>;
std::is_constructible<M::value_type, P&&>::value; // false

因为没有可用的构造函数,包括编译器生成的默认隐式移动构造函数。

non_copyable 甚至禁用其默认的隐式移动构造函数的原因是因为有一个用户定义的复制构造函数,如documented

如果没有为类类型(结构、类或联合)提供用户定义的移动构造函数,并且以下所有情况都为真:

没有用户声明的复制构造函数

没有用户声明的复制赋值运算符

没有用户声明的移动赋值运算符

没有用户声明的析构函数

现在给non_copyable 一个移动构造函数成为你的责任:

struct non_copyable {
    non_copyable(){}
    non_copyable(non_copyable &&) = default;

private:
    non_copyable(const non_copyable&);
};

【讨论】:

    【解决方案2】:

    您可能会对为什么必须制作任何副本感兴趣,因此即使您可以直接在地图中构造一对(放置它),也可以提供可复制构造的类型。

    这是因为为了提供所需的性能,std::map 的内部表示通常实现为平衡的二叉搜索树(最终作为具有相似性能特征的另一种数据结构),需要某些特定顺序的存储元素。因此,在添加或删除某些元素后,顺序可能会被破坏,为了保持其正确性,有时需要对存储的元素进行内部移动。

    【讨论】:

      猜你喜欢
      • 2015-04-16
      • 2016-09-07
      • 2012-07-28
      • 1970-01-01
      • 2011-03-16
      • 1970-01-01
      • 2012-04-03
      • 2021-10-22
      • 1970-01-01
      相关资源
      最近更新 更多