[英]Why does `std::pair<int, movable>` require a [deleted] `const&` copy constructor?
I'm busy testing an implementation of various generic algorithms and I'm using types with minimal support of provided functions. 我正忙着测试各种通用算法的实现,而我正在使用对提供的函数支持最少的类型。 I came across this weird setup when using a std::pair<T, movable>
with some type T
(eg, int
) and a movable
type defined like this: 当使用std::pair<T, movable>
和某些类型T
(例如, int
)时,我遇到了这种奇怪的设置,并且使用了这样定义的movable
类型:
struct movable
{
movable() {}
movable(movable&&) = default;
// movable(movable const&) = delete;
movable(movable&) = delete;
};
The idea is have a type which is movable but not copyable. 这个想法有一种可移动但不可复制的类型。 That works great, eg, with expressions like this: 这很好用,例如,使用这样的表达式:
movable m1 = movable();
movable m2 = std::move(m1);
However, when trying to use this type as a member of std::pair<...>
it fails! 但是,当尝试将此类型用作std::pair<...>
的成员时,它会失败! To make get the code to compile it is necessary to add the delete
d(!) copy constructor taking a movable const&
(or have only that version). 为了使代码得到编译,有必要添加delete
d(!)复制构造函数来获取movable const&
(或只有该版本)。 The copy constructor taking a non- const
reference is insufficient: 采用非const
引用的复制构造const
不足:
#include <utility>
auto f() -> std::pair<int, movable> {
return std::pair<int, movable>(int(), movable());
}
What is going on here? 这里发生了什么? Is std::pair<...>
overspecified by mandating that std::pair(std::pair const&)
is = default
ed? std::pair<...>
通过强制要求std::pair(std::pair const&)
= default
编辑来指定?
The problem seems to be down to the specification of std::pair
's copy constructor (in 20.3.2 [pairs.pair] synopsis): 问题似乎是std::pair
的拷贝构造函数的规范(在20.3.2 [pairs.pair]概要中):
namespace std { template <class T1, class T2> struct pair { ... pair(const pair&) = default; ... }; }
A quick check with my implementation implies that the obvious implementation copying the two members does not require the const&
version of the movable
copy constructor. 快速检查我的执行意味着明显实现复制两个成员并不需要const&
该版本movable
的拷贝构造函数。 That is, the offensive part is the = default
on pair
's copy constructor! 也就是说,攻击部分是pair
的复制构造函数的= default
!
std::pair
copy constructor is declared as follows: std::pair
copy构造函数声明如下:
pair(const pair&) = default;
By declaring this copy constructor for movable
: 通过声明此复制构造函数为movable
:
movable(movable&) = delete;
you inhibit implicit creation of movable(const movable&)
(so it's not even deleted, there's just no such constructor), thus this is the only copy constructor you have. 你禁止隐式创建movable(const movable&)
(所以它甚至没有被删除,也就是没有这样的构造函数),因此这是你唯一的复制构造函数。 But std::pair
copy constructor requires a copy constructor of its members to take const reference, so you get compile error. 但是std::pair
复制构造函数需要其成员的复制构造函数来获取const引用,因此会出现编译错误。
If you add this: 如果你添加这个:
movable(movable const&) = delete;
or (better) just remove movable(movable&) = delete;
或(更好)只是删除movable(movable&) = delete;
declaration, you now have the movable(movable const&)
constructor, and because it's deleted, the std::pair
copy constructor also becomes deleted. 声明,你现在有了movable(movable const&)
构造函数,并且因为它被删除了, std::pair
复制构造函数也会被删除。
Update : Let's consider a simpler example demonstrating the same issue. 更新 :让我们考虑一个更简单的例子来说明同样的问题。 This doesn't compile: 这不编译:
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()};
It will compile if you comment the copy constructor of holder
, because this is how implicit copy constructor generation works ( [class.copy]/8
: 如果你注释了holder
的拷贝构造函数,它会编译,因为这是隐式拷贝构造函数生成的工作方式( [class.copy]/8
:
The implicitly-declared copy constructor for a class X will have the form 类X的隐式声明的复制构造函数将具有该表单
X::X(const X&)
if each potentially constructed subobject of a class type
M
(or array thereof) has a copy constructor whose first parameter is of typeconst M&
orconst volatile M&
. 如果每个可能构造的类类型M
(或其数组)的子对象具有复制构造函数,其第一个参数的类型为const M&
或const volatile M&
。 Otherwise, the implicitly-declared copy constructor will have the form 否则,隐式声明的复制构造函数将具有该表单
X::X(X&)
That is, when you comment out the declaration holder(holder const&) = default;
也就是说,当你注释掉声明holder(holder const&) = default;
the implicitly declared copy constructor of holder
will have the form holder(holder&)
. holder
的隐式声明的复制构造函数将具有表单holder(holder&)
。 But if you don't, T
's copy constructor has take const T&
(or const volatile T&
) because this is what will be called in memberwise copy procedure described in [class.copy]/15
. 但是,如果你不这样做, T
的复制构造函数已经采用const T&
(或const volatile T&
),因为这将在[class.copy]/15
描述的成员复制过程中[class.copy]/15
。
And if holder
has a move constructor, it's even easier - if you comment out holder(holder const&) = default;
如果holder
有一个移动构造函数,那就更容易 - 如果你注释掉holder(holder const&) = default;
, the implicitly declared copy constructor of holder
will be just deleted. ,隐藏声明的holder
复制构造函数将被删除。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.