C++17 中复制构造函数的继承

Inheritance of copy constructors in C++17

本文关键字:继承 构造函数 复制 C++17      更新时间:2023-10-16

请考虑以下示例:

struct Parent
{
Parent ();
Parent (const Parent &);
};
struct Child : public Parent
{
using Parent::Parent;
};
Parent p;
Child c (p);

这取自以下问题:为什么"继承的构造函数不是从相同或派生类型的表达式初始化的候选者"?

最初的问题是关于C++11的。在 C++11 中,有一些措辞阻止Child获取需要const Parent&的构造函数:

对于继承构造函数的

候选集合中的每个非模板构造函数,除了没有参数的构造函数或具有单个参数的复制/移动构造函数之外,构造函数将隐式声明具有相同的构造函数特征,除非存在具有相同构造函数的用户声明构造函数 出现using 声明的类中的签名。

N4429 显着更改了继承构造函数的规范,并被认为追溯到 C++11(我认为?N4429 的目的是使基类构造函数可见,就好像它们是派生类构造函数一样,而不是声明委托给基类构造函数的派生类构造函数。在 N4429 的第一个版本中,有以下措辞,将限制从 C++11 开始:

当 using-声明声明类从基类继承构造函数

时,基类的默认构造函数、复制构造函数和移动构造函数(如果有(将从引入的声明集中排除。

然而,P0136R0,在本文的更新版本中,这一措辞已不复存在,也没有解释原因。该论文再次修订,然后合并到标准中。因此,在 C++17 中,我看不到任何会阻止上述代码编译的规则。

尽管如此,GCC和Clang都拒绝了它。叮当 说:

继承的构造函数不是从相同或派生类型的表达式初始化的候选项

但是,我在标准中找不到任何这样的说法。

此代码在 C++17 中格式不正确吗?如果是这样,为什么?

[over.match.funcs]/8:

从类类型C([class.inhctor.init]( 继承的构造函数具有类型为"对 cv1P的引用"的第一个参数(包括从模板实例化的此类构造函数(在构造cv2类型的对象时D如果参数列表只有一个参数并且CP引用相关,并且PD引用相关。

请参阅CWG2356。

我认为WG21的规则是:

基类复制和移动构造函数引入派生类 在以下情况下,重载解析不考虑使用声明 构造派生类对象。

如果 Base 的继承构造函数之一恰好具有与派生的复制/移动构造函数匹配的签名,则不会阻止派生复制/移动构造函数的隐式生成。

请注意,using A::A;指令继承所有构造函数,包括复制/移动和编译器生成的A的默认构造函数,到出现此指令的派生类中;在查找派生类的构造函数或形成候选集时,将考虑 A 的所有构造函数(复制/移动和编译器生成的默认构造函数除外(。

struct B { B(); };
struct D : B { using B::B; };
D d{ B() }; // error: overload resolution found nothing. #1
D d{ D() }; // OK #2

默认的复制构造函数B::B(const B&)继承到D但此构造函数不是候选函数。如果没有 using 指令,程序的格式正确,因为这被视为聚合初始化。此外,using-声明不会阻止编译器生成D的默认复制/移动构造函数,这就是为什么#2是可以的。

现在为了抑制此错误,我们将显式提供一个用户提供的复制构造函数:

struct B{
B();
};
struct D : public B{
using B::B;
B(const B&);
};
D d{ B() }; // OK: overload resolution found constructor matches the argument-list.