我正忙着测试各种通用算法的实现,而我正在使用对提供的函数支持最少的类型。当使用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}}复制构造函数!
答案 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>