【问题标题】:C++: Is It OK to Inherit from a Class and Its Protected Member Type?C++:可以继承类及其受保护的成员类型吗?
【发布时间】:2018-09-03 11:29:23
【问题描述】:

以下代码是否符合 C++ 标准?

struct B
{
protected:
    struct Type {};
};

struct D : B, B::Type
{};

int main()
{
    D d;
    return 0;
}

我在Compiler Explorer 上试过。 MSVC(VS 2017 RTW)接受它。 gcc(7.3) 和 clang(6.0.0) 拒绝它。

【问题讨论】:

  • 不知道,但您已经证明,无论标准怎么说,您都不能依赖行为,所以不要。一种解决方法是只使用两个级别的类派生(继承)。

标签: c++ inheritance multiple-inheritance protected


【解决方案1】:

如果 D 从 B 派生,它可以访问其受保护的成员,所以这应该是正确的。问题是它在编译时是否已经访问,或者它是否只有在类型完成后才能访问。

请注意,如果将两者翻转(将 B 放在逗号后面),它应该会失败。

【讨论】:

  • 事实上,如果碱基以其他顺序列出,它不应该失败:见我的回答。
【解决方案2】:

代码符合标准,从 C++11 开始,但不在 C++03 中。

C++11 到 C++17 在 [class.access] 部分的介绍中这样说,成员访问控制:

子句 [class.access] 中的所有访问控制都会影响从特定实体的声明中访问类成员名称的能力,包括被声明实体名称之前的声明部分,如果该实体是类,出现在类的成员规范之外的类成员的定义。

在这些相同的标准版本中,以下示例与您的问题非常相似,但更棘手:

[示例:

  class A {
...
  protected:
      struct B { };
  };
...

  struct D: A::B, A { };

...A::B 作为 base-specifier 的使用是正确的,因为 D 派生自 A,所以检查 base-specifiers em> 必须推迟到整个 base-specifier-list 被看到。 -结束示例]

但是我看到了相同的结果:g++ 和 clang++ 都拒绝这些程序,无论我给出什么 -std= 参数。这是一对编译器错误。

C++03 有这个而不是我上面引用的第一段:

子句 [class.access] 中的所有访问控制都会影响从特定范围访问类成员名称的能力。对出现在成员类定义之外的类成员定义中使用的名称进行访问控制,就好像整个成员定义出现在成员类的范围内一样......

类定义的 base-specifier 不在该类的范围内,因此 C++03 不允许使用受保护或私有名称作为派生类的基类名称否则有权访问该名称。

【讨论】:

  • 这是另一个tricky case,如果你已经这样做了,你就可以继承它。
  • @DavisHerring 你认为这个程序格式不正确吗?我很难理解为什么会有所不同。
  • 它属于“当且仅当它是良构的时候它是良构的”的精彩范畴:如果C继承自A::B,那么它继承来自A,因此可以提及A::B 来继承它。如果没有,它不能。任何一种状态都是自洽的,并且 GCC/Clang 似乎正在做添加一种时间组件的常见事情(以您引用的“延迟”为模):您从 0 个基类“开始”,并且没有路径拥有1 根据与 0 相关的规则。我认为这里的措辞没有任何明确的含义。
  • @DavisHerring 您正在使用隐含的假设,即“如果基本说明符列表中的名称不可访问,则该类不会继承命名类型”。但是基类或继承或成员访问的描述并不直接支持这一点。我想说说成员访问检查总是与所有其他语义考虑因素正交更简单,而且总是正确的。我们不会说使用不可访问的成员实际上并没有命名、使用或调用该成员......
  • ... 那么为什么我们不能说“Z 继承 X::Y 的格式错误的程序,并且使用 X::Y 是格式错误的,因为成员名称是无法访问”?
猜你喜欢
  • 2014-01-30
  • 2016-06-12
  • 2014-06-09
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-20
  • 2010-09-14
相关资源
最近更新 更多