C++17中拷贝构造函数的继承

Inheritance of copy constructors in C++17

考虑以下示例:

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&:

的构造函数

For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same signature in the class where the using-declaration appears.

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

When a using-declaration declares that a class inherits constructors from a base class, the default constructor, copy constructor, and move constructor (if any) of the base class are excluded from the set of introduced declarations.

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

尽管如此,GCC and Clang both reject it。铿锵声说:

an inherited constructor is not a candidate for initialization from an expression of the same or derived type

但是,我在标准中找不到这样的内容。

此代码在 C++17 中是否格式错误?如果是,为什么?

[over.match.funcs]/8:

A constructor inherited from class type C ([class.inhctor.init]) that has a first parameter of type “reference to cv1 P” (including such a constructor instantiated from a template) is excluded from the set of candidate functions when constructing an object of type cv2 D if the argument list has exactly one argument and C is reference-related to P and P is reference-related to D.

参见CWG2356

我认为 WG21 的规则是:

Base class copy and move constructors brought into a derived class via a using-declaration are not considered by overload resolution when constructing a derived class object.

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

请注意,using A::A; 指令 继承 所有构造函数,包括 copy/move 和 compiler-generated 默认构造函数 A 到派生class(s) 该指令出现的地方;以及 A 的所有构造函数, 除了 copy/move 和 compiler-generated 默认构造函数,在查找派生 class 的构造函数或形成集合时被考虑候选人。

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 指令,程序是 well-formed 因为这被认为是聚合初始化。此外 using-declaration 不会阻止编译器生成 D 的默认 copy/move 构造函数,这就是为什么 #2 可以。

现在为了抑制这个错误,我们应该明确地提供一个user-provided复制构造函数:

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.