为什么'std::p air<int, movable>'需要一个[已删除的]'const&'复制构造函数?
Why does `std::pair<int, movable>` require a [deleted] `const&` copy constructor?
我正忙于测试各种通用算法的实现,并且我正在使用对所提供函数支持最少的类型。当使用具有某种类型T
(例如,int
)的std::pair<T, movable>
和定义如下的movable
类型时,我遇到了这种奇怪的设置:
struct movable
{
movable() {}
movable(movable&&) = default;
// movable(movable const&) = delete;
movable(movable&) = delete;
};
这个想法是有一个可移动但不可复制的类型。这很好用,例如,对于这样的表达式:
movable m1 = movable();
movable m2 = std::move(m1);
但是,当尝试将此类型用作std::pair<...>
的成员时,它会失败!要获取要编译的代码,有必要添加采用movable const&
(或仅具有该版本)的 delete
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
的复制构造函数的规范(在 20.3.2 [pairs.pair] 概要中):
namespace std { template <class T1, class T2> struct pair { ... pair(const pair&) = default; ... }; }
快速检查我的实现意味着复制两个成员的明显实现不需要movable
复制构造函数的const&
版本。也就是说,攻击部分是pair
复制构造函数上的= default
!
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
中描述的成员复制过程中调用。
如果holder
有一个移动构造函数,那就更容易了 - 如果你注释掉holder(holder const&) = default;
,隐式声明的holder
复制构造函数将被删除。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 从链接列表c++中删除一个项目
- G++ 发出警告,要求删除一个代码的数组,但不删除另一个代码的数组
- 除了调用全局删除运算符之外,删除一个void指针还能做什么呢
- 仅从无序集合中删除一个项目
- C++ 从数组中删除一个最大值和零
- 如果一个类继承了一个带有虚拟 dtor 的类,为什么要删除一个没有虚拟 dtor 的类是可以的
- 如何在不使用循环或迭代器的情况下检查所有列表元素并在满足条件时删除一个
- 我只想从文本文件中删除一个特定值或行
- C 对象阵列删除一个
- 在包含相同项目的向量中删除一个项目
- 当Ccarray_foreach中删除一个对象时,就会发生问题
- 从C++二进制搜索树中删除一个节点(类而非结构)
- C 从整数删除一个数字
- 如何从C 中的对象向量中删除一个项目
- 在单个链接列表中间删除一个节点,仅给定对该节点的访问
- 很难从我的二进制搜索树中删除一个节点
- 添加两个节点并在比较时删除一个节点
- C++-从单链循环列表中的任何索引中删除一个节点
- 如何从结构数组中删除一个数据行?(带索引)