为什么`std :: pair <int,movable =“”>`需要一个[删除]`const&amp;`复制构造函数?</int,>

时间:2014-12-23 19:39:52

标签: c++ c++11 stl c++-standard-library

我正忙着测试各种通用算法的实现,而我正在使用对提供的函数支持最少的类型。当使用std::pair<T, movable> T类型int(例如movable)和struct movable { movable() {} movable(movable&&) = default; // movable(movable const&) = delete; movable(movable&) = delete; }; 类型定义时,我遇到了这种奇怪的设置:

movable m1 = movable();
movable m2 = std::move(m1);

这个想法有一种可移动但不可复制的类型。这很有效,例如,使用这样的表达式:

std::pair<...>

但是,当尝试将此类型用作delete的成员时,它会失败!为了使代码得到编译,有必要添加movable const& d(!)复制构造函数,使用const(或只有该版本)。采用非#include <utility> auto f() -> std::pair<int, movable> { return std::pair<int, movable>(int(), movable()); } 引用的复制构造函数不足:

std::pair<...>

这里发生了什么?授权std::pair(std::pair const&) = default是否过度指定std::pair

问题似乎取决于 namespace std { template <class T1, class T2> struct pair { ... pair(const pair&) = default; ... }; } 复制构造函数的规范(在20.3.2 [pairs.pair]概要中):

const&

使用我的实现进行快速检查意味着复制两个成员的明显实现 not 需要movable版本的= default复制构造函数。也就是说,令人反感的部分是pair的{​​{1}}复制构造函数!

1 个答案:

答案 0 :(得分:8)

std::pair复制构造函数声明如下:

pair(const pair&) = default;

通过为movable声明此复制构造函数:

movable(movable&) = delete;

你禁止隐式创建movable(const movable&)(所以它甚至没有被删除,只是没有这样的构造函数),因此这是你拥有的唯一拷贝构造函数。但是std::pair复制构造函数需要其成员的复制构造函数来获取const引用,因此会出现编译错误。

如果你添加:

movable(movable const&) = delete;

或(更好)只需删除movable(movable&) = delete;声明,您现在拥有movable(movable const&)构造函数,并且因为它已被删除,std::pair复制构造函数也会被删除。

更新:让我们考虑一个更简单的示例来演示相同的问题。这不会编译:

template <typename T>
struct holder {
    T t;
    // will compile if you comment the next line
    holder(holder const&) = default;
    // adding or removing move constructor changes nothing WRT compile errors
    // holder(holder&&) = default;
};

struct movable {
    movable() {}
    movable(movable&&) = default;
    // will also compile if you uncomment the next line
    //movable(movable const&) = delete;
    movable(movable&) = delete;
};

holder<movable> h{movable()};

如果您对holder的复制构造函数进行注释,它将进行编译,因为这是隐式复制构造函数生成的工作原理([class.copy]/8

  

类X的隐式声明的复制构造函数将具有

形式      

X::X(const X&)

     

如果每个可能构造的类类型M(或其数组)的子对象具有复制构造函数,其第一个参数的类型为const M&const volatile M&。否则,隐式声明的复制构造函数将具有

形式      

X::X(X&)

也就是说,当您注释掉声明holder(holder const&) = default;时,holder的隐式声明的复制构造函数将具有holder(holder&)形式。但是如果你不这样做,T的复制构造函数已经取const T&(或const volatile T&),因为这将在[class.copy]/15中描述的成员复制过程中调用。 {1}}。

如果holder有一个移动构造函数,那就更容易了 - 如果你注释掉holder(holder const&) = default;holder的隐式声明的复制构造函数将被删除。< / p>