首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有正向声明的嵌套类会在clang++中导致错误,但在g++上传递时不会发出警告

具有正向声明的嵌套类会在clang++中导致错误,但在g++上传递时不会发出警告
EN

Stack Overflow用户
提问于 2020-11-18 23:18:55
回答 2查看 152关注 0票数 7

我试图将嵌套类和转发声明结合起来,以保持代码的易读性,即使它有一个非常复杂的类结构。正向声明使我可以减少这里的缩进级别。

下面的代码在g++-9.3和clang++-10上都能很好地编译:

代码语言:javascript
复制
class A {
    public:
        class B;
};
class A::B {
    public:
        int foo=0;
};

但是,当我在另一个类中执行相同的嵌套操作时,此构造在g++上工作时没有任何警告,但在clang++上失败:

代码语言:javascript
复制
class Outer {
   public:
      class A {
         public:
            class B;
      };
      class A::B {
         public:
            int foo=0;
      };
};

clang++失败的原因是:

代码语言:javascript
复制
test.cpp:7:16: error: non-friend class member 'B' cannot have a qualified name
      class A::B {
            ~~~^
1 error generated.

我猜这在某种程度上是无效的代码,gcc是仁慈的,能够正确地解释吗?我意识到我可以直接在类A中移动类定义,但是假设我想保留这个带有向前声明的版本。

EN

回答 2

Stack Overflow用户

发布于 2020-11-18 23:38:34

其class-head-name包含嵌套名称说明符的class说明符不能出现在类中;只能出现在封闭的命名空间中

来自[class]/11

如果类头名称包含使用说明符,则类说明符应引用先前直接在 nested-name-specifier 引用的类或命名空间中声明的类,或者在该命名空间的内联命名空间集合的元素中声明的类(即,不仅仅是由声明继承或引入的),和 class-specifier名称应该出现在包含前一个声明的命名空间中。在这种情况下,定义的类头名称的嵌套名称说明符不能以decltype说明符开头。

在失败的示例中,A::是嵌套名称说明符,A::B { ... }是类说明符。标准

...类说明符应该在包含前一个声明的命名空间中出现为。

没有得到满足。特别要注意的是,class说明符包含class-head,而class-head-name又包含class-head-name,而class-head-name又可选地包含嵌套名称说明符,以及(非可选) class-name。如果存在嵌套名称说明符,则应用class/11,在这种情况下(如上所述),将应用允许在何处使用类说明符的要求;特别是,不允许嵌套在类中。

因此,你的程序是病态的;Clang是正确的。

我们可能会注意到,如果B的类指定符出现在名称空间作用域(包含先前的类内声明),则Clang接受该程序:

代码语言:javascript
复制
class Outer {
 public:
    class A {
      public:
        class B;
    };
};

class Outer::A::B {
  public:
    int foo=0;
};

最后,请注意,[class.nest]/3提到了在声明嵌套类的类的类中定义嵌套类,以及在封闭命名空间的命名空间范围内定义嵌套类:

如果在命名空间范围中定义类X,则嵌套类Y可以在类X中声明,稍后在类X的定义中定义,或者稍后在包含类X的定义的命名空间范围中定义。_ Example:_

类E{类I1;//正向声明嵌套类类I2;类I1 { };//定义嵌套类};类E::I2 { };//定义嵌套类

- end示例 

这是你的第一个例子:

类A{ public:类B;};类A::B { public: int foo=0;};

被重构为

代码语言:javascript
复制
class A {
  public:
    class B {
      public:
        int foo=0;
    };
};

代码语言:javascript
复制
class A {
  public:
    class B;  // forward declaration
    // ...
    class B {
      public:
        int foo=0;
    };
};

但是这里与class/11没有冲突,当class-head-name包含嵌套名称说明符时,class/11专门管理所允许的语法。

票数 4
EN

Stack Overflow用户

发布于 2020-11-18 23:34:32

我认为代码是有效的,这是一个clang bug。根据class.qual#1的说法

如果限定id的嵌套名称说明符指定了一个类,则在类(class.member.lookup) 的作用域中查找在嵌套名称说明符之后指定的名称,下面列出的情况除外。该名称应表示该类或其基类之一(class.derived)的一个或多个成员。

..。

上述名称查找规则的例外情况如下:

(1.1)析构函数的查找如basic.lookup.qual中所指定;

(1.2)转换函数id的转换类型id的查找方式与类成员访问中的转换类型id相同(参见basic.lookup.classref);

(1.3)在整个后缀表达式出现的上下文中查找模板id的模板参数中的名称;

(1.4)查找在使用声明(namespace.udecl)中指定的名称时,还会查找隐藏在同一范围内的类或枚举名称。

所有例外都不适用于A::B的情况。没有要命名的析构函数,没有转换类型id,没有模板,也没有使用声明。

A在作用域中,而BA的可访问成员,因此查找应该会成功。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64895983

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档