假设你有以下类:
struct A {
A () {}
A (A &) = delete;
};
int main() {
std::pair<A, int> p1;
return 0;
}
以下代码将无法编译(将-std=c++11
与 g++
一起使用(,并显示以下错误:
/usr/include/c++/5/bits/stl_pair.h:在实例化"struct std::p air"时:
test.cpp:13:23:从这里需要
/usr/include/c++/5/bits/stl_pair.h:127:17:错误:"constexpr std::p air<_T1, _T2>::p air(const std::p air<_T1, _T2>&( [带有 _T1 = A; _T2 = int]' 声明接受 const 引用,但隐式声明将采用 non-const
constexpr pair(const pair&) = default;
根据错误消息,我认为这是因为由于std::pair
参数上的const
限定符,无法实例化默认复制构造函数。
我可以理解为什么没有= delete
就无法编译,因为不可能实例化采用std::pair const&
参数的复制构造函数。
但是对于= delete
,我希望编译器不会实例化这样的构造函数,因为它不能(据我所知(。实际上,此复制构造函数被删除,如以下代码所示:
std::pair<A, int> p1;
decltype(p1) p2(p1);
失败:
test.cpp:11:23:错误:使用已删除的函数"constexpr std::p air<_T1, _T2>::p air(const std::p air<_T1, _T2>&( [_T1 = A; _T2 = int]'
decltype(p1) p2(p1);
基本上,我的问题是:为什么编译器无法实例化已删除的std::pair
复制构造函数?
[class.copy]/8:
类
X
的隐式声明的复制构造函数将具有 形式X::X(const X&)
如果类类型的每个潜在构造子对象
M
(或 数组(具有一个复制构造函数,其第一个参数的类型为const M&
或const volatile M&
.否则,隐式声明 复制构造函数将具有以下形式X::X(X&)
因此,如果要隐式声明std::pair<A, int>
的副本构造函数,它将具有 pair::pair(pair &)
的形式。
[dcl.fct.def.default]/1:
显式默认的函数应
是一个特殊的成员函数,
具有相同的声明函数类型(除了可能不同的 ref 限定符s,除了在复制构造函数或 复制赋值运算符,参数类型可以是"引用 non-const
T
",其中T
是成员函数类的名称( 作为 如果它被隐含地声明,并且没有默认参数。
由于声明pair(const pair&) = default;
与隐式声明的复制构造函数不具有相同的声明函数类型,并且这两个异常都不适用,因此程序格式不正确。
如果要= delete
复制构造函数,正确的形式是:A(const A&) = delete;
。看看你怎么忘记了那const
? pair
可以与不可复制的类型一起使用,甚至可以与不可移动的类型一起使用。
您的实例化问题也可以通过以下方式演示:
struct A {
A(A&) = delete;
};
struct B : A {
B(const B&) = default;
};
或。。。
struct B {
B(const B&) = default;
A a;
};
基本上,pair
= default
它的复制 ctor,并将其定义为采用 const pair&
,但是您的一种类型将其定义为采用非 const &,因此默认生成失败,因为它无法将其 const&传递给您的非 const&。即使你的是= delete
的,也没关系,它不会走那么远。
如果成员或基类不可复制,则默认复制 ctor 将有效删除。但是为了弄清楚这一点,它必须有一个能够调用的函数(即使该函数被删除(。在这些情况下,它不能将 const&传递给非 const&,因此它甚至无法确定您有一个已删除的副本 ctor。可以想象,可以编写标准中的一些内容来适应这种边缘情况。