在一个类中,`使用Base :: BaseOfBase;应该做什么?

问题描述 投票:4回答:1

考虑此代码:

#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(),则C c(1);不再编译(GCC找不到合适的构造函数)

Clang没有说什么using B::A;,但是拒绝编译C c(1);(也找不到合适的构造函数)。

[MSVC就在using B::A;处停止,基本上是说您只能从直接基继承构造函数。

Cppreference并未提及从间接基继承构造函数,因此似乎是不允许的。

是GCC和Clang错误,还是这里发生了什么?

c++
1个回答
4
投票

我可以说,某些GCC继承该构造函数是错误的。主要是因为

[namespace.udecl]

3在用作成员声明的using声明中,每个 using-declarator的nested-name-specifier应命名为 被定义的类。如果using-declarator为构造器命名, 其嵌套名称说明符应命名该类的直接基类 正在定义。

但是最重要的是B::A甚至都没有命名构造函数。

[[class.qual]

2在其中不忽略函数名称的查找中 nested-name-specifier提名一个类C:

  • 如果在C中查询在嵌套名称说明符之后指定的名称,则是C的注入类名称(Clause [class]),或
  • 在作为成员声明的using声明的using声明中,如果名称在 nested-name-specifier与标识符或 simple-template-id的模板名称在 嵌套名称说明符,

该名称被认为是为类C的构造函数命名。 [注意:例如,构造函数不是可接受的查找 导致一个详尽的类型说明符,因此构造函数将不会 用于代替注入的类名。 —尾注]这样的 构造函数名称应仅在a的declarator-id中使用 声明构造函数或使用声明的名称。 [示例:

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);

哪个显示正确。唯一的用途是B是否继承自protected A。在这种情况下,默认情况下,注入的类名也将不可访问,直到使用using声明提出。在godbolt上修改您的示例即可确认。

MSVC可能对拒绝此代码非常热心。

© www.soinside.com 2019 - 2024. All rights reserved.