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

Inheritance of copy constructors in C++17

提问人:Brian Bi 提问时间:9/13/2019 最后编辑:curiousguyBrian Bi 更新时间:5/9/2022 访问量:953

问:

请看以下示例:

struct Parent
{
    Parent ();

    Parent (const Parent &);
};

struct Child : public Parent
{
    using Parent::Parent;
};

Parent p;

Child c (p);

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

最初的问题是关于C++11的。在 C++ 11 中,有一些措辞阻止获取采用以下方式的构造函数:Childconst Parent&

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

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

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

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

尽管如此,海湾合作委员会和Clang都拒绝了它。Clang 说道:

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

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

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

C++ 继承 语言律师 C++17 复制构造函数

评论

1赞 Barry 9/14/2019
有趣的是,我回答了这个问题这个问题但仍然找不到规则......

答:

10赞 T.C. 9/14/2019 #1

[over.match.funcs]/8

从类类型 ([class.inhctor.init]) 继承的构造函数,如果参数列表只有一个参数,并且与引用相关,并且与引用相关,则从类类型 ([class.inhctor.init]) 继承的构造函数(包括从模板实例化的此类构造函数)中排除。CPDCPPD

请参见CWG2356

评论

1赞 Brian Bi 9/14/2019
这被认为是对 C++17 的追溯,对吧?
2赞 T.C. 9/14/2019
是的,这是一个 DR。
0赞 Brian Bi 9/14/2019
您是否碰巧知道为什么决议不是简单地复活已删除的段落,以防止此类构造函数被继承?此解决方案是否以某种方式允许这些构造函数在其他上下文中保持可见?
3赞 T.C. 9/14/2019
在重载解决时间之前,您无法判断构造函数模板最终的样子。
1赞 mada 5/2/2022 #2

我认为WG21的规则是:

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

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

请注意,directive 继承了所有构造函数,包括 copy/move 和编译器生成的默认构造函数,进入出现此指令的派生类;在查找派生类的构造函数或形成候选集合时,会考虑 A 的所有构造函数,复制/移动和编译器生成的默认构造函数除外。using 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

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

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

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.