将内部类名声明为另一个声明的一部分

时间:2016-05-13 22:04:22

标签: c++ language-lawyer

在尝试用pimpl习惯用法定义一个类时,我尝试通过将类前向声明​​滚动到pimpl定义来保存一行代码。

class A
{
public:
    class Impl *pimpl;
};

以上声明编辑好。什么行不通,是定义 A::Impl的尝试。 As the program bellow demonstrates

class A
{
public:
    class Impl *pimpl;
};

class A::Impl
{

};

int main() {
    // your code goes here
    return 0;
}

结果如下:

  

错误:限定名称未在“{”标记

之前命名类

现在,可以通过精心设计的类型说明符引入新的类名作为另一个声明的一部分。但它实际上是Class ::Impl introduced

class A
{
public:
    class Impl *pimpl;
};

class Impl
{

};

int main() {
    Impl i;
    A a;
    a.pimpl = &i;
    return 0;
}

为什么它自己的行上的前向声明(经常使用成语)会引入A::Impl,但是当声明为指针定义的一部分时,会引入::Impl?< / p>

更新

我不是在问这个成语是如何工作的。我的问题是为什么单行

class Impl *pimpl;

与两行

的效果不同
class Impl;
Impl *pimpl;

在类定义的上下文中。

我感到困惑,因为something similar is done in the context of a namespace时名称已合格。

namespace A
{
    class Impl *pimpl;
};

class A::Impl
{

};

int main() {
    // your code goes here
    return 0;
}

2 个答案:

答案 0 :(得分:5)

稍微更改您的A声明:

class A
{
public:
    class Impl;

    Impl *pimpl;
};

现在,一切都会按预期运作。

首先必须告诉编译器Impl是一个内部类。

class A
{
public:
    class Impl *pimpl;
};

这告诉编译器pimpl是一个指向名为Impl的顶级类的指针,而不是内部类。

答案 1 :(得分:1)

嗯,标准像往常一样提供答案。并明确规定。

引用3.4.4 [basic.lookup.elab]第2段(empahsis mine):

  

如果 elaborated-type-specifier 没有嵌套名称说明符,而除非详细说明类型说明符出现在声明中,格式如下

     

class-key attribute-specifier-seqopt identifier;

     

根据[basic.lookup.unqual] 查找标识符,但忽略任何已声明的非类型名称   (...)
  如果 class-key 引入 elaborated-type-specifier ,并且此查找未找到先前声明的类型名称,或者 elaborated-type-specifier 出现在声明中,格式为:

     

class-key attribute-specifier-seqopt identifier;

     

elaborated-type-specifier 是一个声明,它引入了类名称,如 [basic.scope.pdecl]中所述。

3.3.2 [basic.scope.pdecl]第7段(再次强调我的):

  

首先在 elaborated-type-specifier 中声明的类的声明点如下:

     

- 表格

的声明      

class-key attribute-specifier-seqopt identifier;

     

标识符在包含声明的作用域中声明为类名,否则为

     

- 表格

elaborated-type-specifier      

类密钥标识符

     

如果在命名空间作用域中定义的函数的 decl-specifier-seq parameter-declaration-clause 中使用了elaborated-type-specifier,则标识符为在包含声明的命名空间中声明为类名; 否则,除了作为朋友声明之外,标识符在包含声明的最小名称空间或块范围中声明。 [注意:这些规则也适用于模板。 - 尾注] [注意:其他形式的elaborated-type-specifier不声明新名称,因此必须引用现有的类型名称。参见[basic.lookup.elab]和[dcl.type.elab]。 - 结束说明]

最后一个强调行中的声明似乎引用了详细说明的类说明符出现的类的声明。因此,在我们的示例中,它将Impl添加到包含类A(全局范围)的命名空间中。但是同样适用于任何命名空间。 Case and point

namespace E
{
    class A
    {
        class Impl *pImpl;  
    };
}

class E::Impl
{

};

int main() {
    // your code goes here
    return 0;
}