Die*_*ühl 18 c++ stl c++-standard-library c++11
我正忙着测试各种通用算法的实现,而我正在使用对提供的函数支持最少的类型.当使用std::pair<T, movable>某种类型T(例如int)和movable类似定义的类型时,我遇到了这种奇怪的设置:
struct movable
{
movable() {}
movable(movable&&) = default;
// movable(movable const&) = delete;
movable(movable&) = delete;
};
Run Code Online (Sandbox Code Playgroud)
这个想法有一种可移动但不可复制的类型.这很好用,例如,使用这样的表达式:
movable m1 = movable();
movable m2 = std::move(m1);
Run Code Online (Sandbox Code Playgroud)
但是,当尝试使用此类型作为其成员std::pair<...>时失败!为了使代码得到编译,有必要添加deleted(!)复制构造函数来获取movable const&(或只有该版本).采用非const引用的复制构造函数是不够的:
#include <utility>
auto f() -> std::pair<int, movable> {
return std::pair<int, movable>(int(), movable());
}
Run Code Online (Sandbox Code Playgroud)
这里发生了什么?是std::pair<...>通过强制要求overspecified std::pair(std::pair const&)被= default编?
问题似乎std::pair取决于复制构造函数的规范(在20.3.2 [pairs.pair]概要中):
Run Code Online (Sandbox Code Playgroud)namespace std { template <class T1, class T2> struct pair { ... pair(const pair&) = default; ... }; }
快速检查我的执行意味着明显实现复制两个成员并没有要求const&该版本movable的拷贝构造函数.也就是说,攻击部分是= defaulton pair的复制构造函数!
std::pair 复制构造函数声明如下:
pair(const pair&) = default;
Run Code Online (Sandbox Code Playgroud)
通过声明此复制构造函数movable:
movable(movable&) = delete;
Run Code Online (Sandbox Code Playgroud)
你禁止隐式创建movable(const movable&)(因此它甚至没有被删除,只有没有这样的构造函数),因此这是你唯一的复制构造函数.但是std::pair复制构造函数需要其成员的复制构造函数来获取const引用,因此会出现编译错误.
如果你添加这个:
movable(movable const&) = delete;
Run Code Online (Sandbox Code Playgroud)
或者(更好)只是删除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()};
Run Code Online (Sandbox Code Playgroud)
如果你注释复制构造函数,它将编译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将被删除.
| 归档时间: |
|
| 查看次数: |
1803 次 |
| 最近记录: |