为什么嵌套类不能有一个类型为封闭类的成员?

Why a nested class can't have a member the type of which is the one of the enclosing class?

本文关键字:成员 类型 嵌套 不能 有一个 为什么      更新时间:2023-10-16

C的方法之一需要返回一个包含一对整数和C的新实例的struct。它可能看起来很尴尬,但考虑到整体设计,这很有意义(想想一个Waveform类将自身的一个范围作为副本返回,并指示范围的开始和结束位置)。

问题是这似乎是不允许的。我可以重新设计我的类以规避这个问题,但你能解释一下为什么从编译器的角度来看,这不能做到

struct S {
    struct S2 {
        S s;
    };
};

因为S是一个不完整的类型(这是编译器错误),相反,这很好

struct C {
    struct C1 {
        C makeC() { return C(); }
    };
};

实质性差异在哪里?

在你尝试定义S::S2时,类型 S 仍然是一个不完整的类型,因为它的定义尚未完成。并且类数据成员必须具有完整的类型。

您可以像这样轻松修复它:

struct S
{
    struct S2;   // declare only, don't define
    // ...
};
struct S::S2
{
    S s;         // now "S" is a complete type
};

它本质上是一个C++的设计决策,在定义结束之前不考虑一个类型是完整的。这可以防止许多病理情况,如以下示例所示:

struct X
{
    struct Y { X a; };
    int b[sizeof(Y)];      // "sizeof" requires a complete type
};

当你想要定义一个类时,你需要一个所有嵌入(非引用和非指针)成员的完整定义。定义类时,未完全定义类。也就是说,在类定义中,定义的类只是声明的,而不是定义的。

也就是说,您仍然可以在嵌套类中拥有类的成员。你只需要在定义外部类之后定义嵌套类:

struct S {
    struct S2;
};
struct S::S2 {
    S s;
};

由于S完全由第一个右大括号定义,因此在此点之后可以将其用作嵌入构件。

在类定义

中定义成员函数时,定义将被解析为紧跟在最接近的命名空间级别的类定义之后。也就是说,成员函数定义知道完全定义的类。这同样不适用于嵌套类。

我不知道

为什么,但我可以推测这个反例:

struct S {
    struct S2 {
        S s;
    };
    S2 s2;
};

在这种情况下,尝试在扫描S并确定其大小之前S2将失败。我怀疑递归可能会给编译器带来困难,而这种困难通常是标准中规则的原因。

也就是说,我认为您的解决方案不太适合您所描述的问题。我会在S上制作S2模板,无论如何都不会嵌套。然后,我将能够对模板参数使用引用S&,而不是S本身。因此,范围将由两个整数和对原始数组对象的引用表示。从此范围制作新副本将是一个单独的操作。

编译器无法计算封闭类的大小。

不过你可以写S* s;

这个答案侧重于你的两个例子之间的区别,因为其他答案已经解释了为什么第一个不起作用。

简而言之,您的第一个示例以需要S才能完成的方式使用S中的S

第二个示例要求C在编译函数体后立即完成。您的代码等效于

struct C {
    struct C1 {
        C makeC();
    };
};
inline C C::C1::makeC() {
    return C();
}

这意味着,编译器会自动"推迟"函数体。对于函数声明,编译器只需要知道C是一种类型,但如果C不完整,它就可以工作,直到那时。编译函数定义时,类型C现已完成。