考虑以下代码:
#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上进行编译并打印A(1)
,这意味着的一个实例B
是“构造的”而不调用构造函数。如果取消注释C()
,则C c(1);
不再编译(GCC找不到合适的构造函数)
Clang没有说任何有关的信息using B::A;
,但是拒绝编译C c(1);
(也找不到合适的构造函数)。
MSVC停在using B::A;
,基本上就是说您只能从直接基础继承构造函数。
Cppreference没有提到从间接基继承构造函数,因此似乎是不允许的。
是GCC和Clang错误,还是这里发生了什么?
我可以说对于某些GCC继承该构造函数是错误的。主要是因为
[namespace.udecl]
3在用作成员声明的using声明中,每个using声明者的nested-name-specifier应命名所定义类的基类。如果using声明符为构造函数命名,则其nested-name-specifier应命名所定义类的直接基类。
但是最重要的是,B::A
它甚至根本没有命名构造函数。
[class.qual]
2在不忽略函数名称且嵌套名称说明符指定类C的查找中:
- 如果在C中查找嵌套名称说明符之后指定的名称是C的注入类名称(子句[class]),或者
- 在使用声明(即成员声明)的使用声明器中,如果在嵌套名称说明符之后指定的名称与标识符或简单模板ID的模板名称在最后一个组成部分中相同嵌套名称说明符,
该名称被认为是为类C的构造函数命名的。 。—结束说明]这样的构造函数名称只能在命名构造函数的声明的declarator-id或using-declaration中使用。[示例:
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
。我想这应该就像从基类中引入任何旧类型定义一样。即Clang让您定义
C::A a(0);
看起来正确。唯一的用途是if是否B
从继承protected A
。在这种情况下,默认情况下,注入的类名也将不可访问,直到使用using声明提出。修补您在godbolt上的示例可以证实这一点。
MSVC可能对拒绝此代码过于热心。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句