为什么std :: insert需要CopyConstructibility?

时间:2018-02-15 08:19:03

标签: c++

为什么这段代码不起作用? 在std :: map的insert方法的文档中没有关于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

2 个答案:

答案 0 :(得分:3)

  

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

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

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

是这个重载的成员函数:

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

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

  

...只有std::is_constructible<value_type, P&&>::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

  

如果没有为类类型(struct,class或union)提供用户定义的移动构造函数,并且满足以下所有条件:

     

没有用户声明的副本构造函数

     

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

     

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

     

没有用户声明的析构函数

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

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

private:
    non_copyable(const non_copyable&);
};

答案 1 :(得分:1)

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

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