C++中类内部的前向声明类

18

下面这段简单的代码可以编译,尽管我不明白为什么:

class C {
    class B;

    class A {
        B getB() { return B(); }
    };

    class B {
    };
};

int main(int, char**)
{
    return 0;
}

如果我注释掉 "class C" 部分,这样B的前向声明、A的定义和B的定义就不再嵌套在类中了,代码将无法编译,因为B是一个不完整的类型:

main.cpp: In member function 'B A::getB()':
main.cpp:6: error: return type 'struct B' is incomplete
main.cpp:6: error: invalid use of incomplete type 'struct B'
main.cpp:3: error: forward declaration of 'struct B'

我明白类型不完整的含义,即它尚未被定义,因此编译器无法知道要分配多少空间。但是,在上面的代码中为什么不认为B是不完整的,其中AB都在C中声明和定义?

4个回答

10
我认为这是[basic.scope.class]的结果:
在类中声明的名称的潜在作用域不仅包括在名称的声明点之后的声明区域,还包括该类中所有非静态数据成员的函数体、默认参数、exception-specificationsbrace-or-equal-initializers(包括嵌套类中的这些内容)。
也就是说,B的完整声明作用域包括嵌套类成员函数的函数体。
class C {
    class B; // (1)

    class A { 
        B getB() {
            return B(); // both (1) and (2) in scope here
                        // since (2) is the complete type declaration,
                        // this is perfectly fine
        }
    };

    class B { // (2)
    };
};

相比之下,如果 C 是一个命名空间而不是一个类,那么类 B 的完整声明的作用域将不会延伸到 A::getB()。唯一可见的声明将是我标记为 (1)B 的前向声明 - 因此在那里 B() 将是不完全类型的构造。


太棒了,感谢您澄清这个问题。引起这个特定问题的原因是libpqxx的result类,其中嵌套了tuplefield类,在其中tuple在其一个函数体中构造了一个field(即第183行)。 - villapx

9

内联成员函数的主体在类定义完全处理之前不会被处理。

因此,您可以使用:

class A 
{
   class B;
   B getB() { return B(); }

   class B {};
};

那也允许在内联成员函数定义中使用尚未声明的成员变量。
class Foo
{
   int getBar() { return bar; }

   int bar;
};

我猜想相同的逻辑也适用于嵌套类成员函数的内联定义,即它们直到包含类定义被完全处理之前不会被处理。
PS:我无法快速找到标准中可以验证我的说法的参考资料。
PS 2:Barry的答案在标准中提供了使问题代码有效的参考资料。

你比我先回答了。我正在尝试找到标准。我认为它被同样地,在类X的成员函数定义中使用的未限定id(5.1)解析为X类或X的基类的静态成员,枚举器或嵌套类型时,未限定id将转换为限定id(5.1),其中嵌套名称指定成员函数的类。所覆盖。来自**[class.mfct.non-static]**。如果你同意,请继续使用它。 - NathanOliver
@NathanOliver 我认为那不对。那只是说会找到 B - 但是由于前向声明,无论如何都会找到 B。我不知道正确的部分在哪里,也找不到它。 - Barry
如果类X在命名空间范围内定义,则可以在类X中声明嵌套类Y,并在类X的定义中稍后定义该嵌套类Y,或者稍后在封闭类X定义的命名空间范围内定义该嵌套类Y。*[class.nest]* - NathanOliver
我还在寻找中。 - R Sahu
@NathanOliver 至少肯定不是在模板子句中。 - Barry
显示剩余2条评论

5
标准明确规定,方法体在包含它的类之后解释执行。因此,在评估 C::A::getB() 的主体时,A、B 和 C 都是完整类型。

你可能知道我在哪里可以找到那个措辞吗? - villapx
1
正在寻找...不幸的是,“成员函数”一词在2015年草案标准PDF中被提及了582次...http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4527.pdf - Richard Hodges

0
除此之外,当我需要前向声明嵌套类时,我倾向于觉得我的代码设计有些糟糕,我使用的技巧是:
// Foo.h
class Foo {
    class Bar {
    };
};
class Foobar : public Foo::Bar {};


// Zoo.h
/* Fwd declare */
class FooBar;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接