考虑这段代码:
#include <iostream>
#include <type_traits>
struct A
{
A(int x) {std::cout << "A(" << x << ")\n";}
};
struct B : A
{
using A::A;
B(int x, int y) : A(x) {std::cout << "B(" << x << "," << y << ")\n";}
};
struct C : B
{
using B::A; // <--
// C() : B(0,0) {}
};
int main()
{
C c(1);
}
gcc.godbolt.org
它在 GCC 上编译并打印A(1)
,这意味着一个实例B
是在没有调用构造函数的情况下“构造”的。如果你取消注释C()
, then C c(1);
不再编译(GCC找不到合适的构造函数)
Clang 没有说什么using B::A;
,但拒绝编译C c(1);
(也找不到合适的构造函数)。
MSVC 正好停在using B::A;
,基本上是说你只能从直接基类继承构造函数。
偏好设置没有提到从间接基继承构造函数,所以它似乎是不允许的。
这是 GCC 和 Clang 的 bug,还是这里发生了什么?
构造函数不是继承的。主要是因为
[命名空间.udecl]
3在用作成员声明的 using 声明中,每个
using-declarator 的嵌套名称说明符应命名一个基类
正在定义的类。如果 using 声明符命名了一个构造函数,
其嵌套名称说明符应命名该类的直接基类
被定义。
但最关键的是B::A
甚至根本没有命名构造函数。
[等级.质量]
2在不忽略函数名称并且
嵌套名称说明符指定一个类 C:
- 如果在 C 中查找时,在嵌套名称说明符之后指定的名称是 C 的注入类名称(子句 [class]),或者
- 在作为成员声明的 using 声明的 using 声明符中,如果在
嵌套名称说明符与标识符或
simple-template-id 的 template-name 在最后一个组件中
嵌套名称说明符,
相反,该名称被视为命名类 C 的构造函数。
[ 注意:例如,构造函数不是可接受的查找
导致一个详细的类型说明符,因此构造函数不会
用于代替注入的类名。 ——《尾注》]这样的
构造函数名称只能在 a 的 declarator-id 中使用
命名构造函数的声明或在 using 声明中。
[ 例子:
struct A { A(); };
struct B: public A { B(); };
A::A() { }
B::B() { }
B::A ba; // object of type A
A::A a; // error, A::A is not a type name
struct A::A a2; // object of type A
—《示例结束》]
上述两条都不适用。所以B::A
不是构造函数的名称。这只是注入的类名A
,它已经可以用于C
。我想这应该就像从基类引入任何旧类型定义一样。 IE。 Clang 会让你定义
C::A a(0);
这看起来是正确的。这样做的唯一用处是如果B
继承自protected A
。在这种情况下,默认情况下注入的类名也将无法访问,直到使用 using 声明提出。修改你的例子godbolt证实了这一点。
MSVC 可能过于热衷于彻底拒绝此代码。
至于哪个编译器是正确的,C++20引入了通过括号内的值列表进行聚合初始化. C
是一个集合体,所以C c(1);
实际上是聚合初始化c
通过使用1
复制初始化B
子对象。所以不需要继承构造函数C
使此代码有效。
GCC 确实正在这样做(因为显式地设置 c'tors 会导致它拒绝代码),而 Clang 似乎还没有实现P0960 yet.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)