c++ 为什么`std :: pair`需要一个[已删除]`const&`copy构造函数?

我正在测试各种通用算法的实现,我使用的类型最少支持所提供的功能。当使用std :: pair< T,可移动>时,我遇到了这个奇怪的设置。具有一些类型T(例如,int)和如下定义的可移动类型:

struct movable
{
    movable() {}
    movable(movable&&) = default;
    // movable(movable const&) = delete;
    movable(movable&) = delete;
};

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

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

但是,当尝试使用此类型作为std :: pair< ...>的成员时它失败!为了得到编译的代码,有必要添加删除的(!)拷贝构造函数,使用一个可移动的const& (或只有那个版本)。使用非常量引用的复制构造函数不足:

#include <utility>
auto f() -> std::pair<int, movable> {
    return std::pair<int, movable>(int(), movable());
}

这里发生了什么?是std :: pair< ...>通过强制执行std :: pair(std :: pair const&)=默认值超标?

这个问题似乎归结于std :: pair的拷贝构造函数的规范(在20.3.2 [pairs.pair]的概要中)):

06003

对我的实现进行快速检查意味着复制两个成员的明显实现不需要const&版本的可移动拷贝构造函数。也就是说,进攻部分是对的副本构造函数的=默认值!

最佳答案
std :: pair copy constructor声明如下:

pair(const pair&) = default;

通过声明这个可移动的复制构造函数:

movable(movable&) = delete;

你禁止隐式创建可移动(const可移动&)(所以它甚至不删除,没有这样的构造函数),因此这是唯一的复制构造函数。但是,std :: pair copy构造函数需要其成员的复制构造函数来引用const引用,所以你得到编译错误。

如果你添加:

movable(movable const&) = delete;

或(更好)只是删除可移动(可移动&)=删除;声明,你现在有可移动(可移动的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()};

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

The implicitly-declared copy constructor for a class X will have the form

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 type const M& or const volatile M&. Otherwise, the implicitly-declared copy constructor will have the form

X::X(X&)

也就是说,当你注释出声明持有人(持有人const&)= default;持有人的隐含声明的复制构造函数将具有表单持有者(持有人&)。但是,如果你没有,T的拷贝构造函数已经取得了const T& (或const volatile T&),因为这将在[class.copy] / 15中描述的成员复制过程中被调用。

如果持有者有一个移动构造函数,它更容易 – 如果你注释了持有人(持有者const&)=默认;隐藏声明的持有者的复制构造函数将被删除。

转载注明原文:c++ 为什么`std :: pair`需要一个[已删除]`const&`copy构造函数? - 代码日志