为什么一个类不能在没有前向声明的情况下具有指向另一个类的实例的指针?

Why can't a class have a pointer to an instance of another class without forward declaration?

本文关键字:情况下 另一个 指针 实例 声明 不能 一个 为什么      更新时间:2023-10-16

我知道做是不可能的

class foo{
    foo bar;
};

因为编译器不知道要为foo准备多少内存。但考虑一下:

class A{
    B::C* foo;
};
class B{
    class C{
    };
};

无论C的定义如何,编译器都会准确地知道A的大小,这似乎很合理。为什么这不可能呢?(标准明确禁止这样做吗?为了什么?)


我得到了一个非常粗略的方法来绕过约束,即将foo视为uintptr_t,然后将reinterpret_cast返回到C,这只是为了表明我真的不明白(客观)原因,我知道如果在没有正当理由的情况下使用,这是一个非常非常糟糕的做法。


最后它被证明是标准所禁止的

一个巨大的挑战如下:

class other {};
namespace N {
  class foo {
    other* p;
  };
  class other {};
}

应考虑哪个other*?编译器工作量太大。尽管我们可以用::otherN::other来显式,但在上述情况下仍然会造成混淆。

TL;DR:那是因为标准规定语法是这样的。不幸的是,它就这么简单。

这是我走的路:

  1. 您打开标准,例如c++11草稿。

  2. 你要找关于课程的部分,这是第9部分。你会立即看到它有第9.2小节"集体成员"。已经定义了完整的语法。经过一段时间的阅读,你可以进入member-declarator。在我们的上下文中最重要的部分是declarator

  3. 第8节。声明人。在p4。您有declarator的语法。在浏览了所有支持的花式语法的正式定义之后,您会注意到它依赖于id-expression

  4. 5.1.1主要表达式对此进行了定义。概述([expr.prim.general])。并且它必须是unqualified-idqualified-id

所以你的问题可以改为"为什么名字查找不起作用?"但这是另一个问题。

FWIW,我想不多,但这是一个设计决定。它需要为汇编单位记录所有失败的决议。在这种情况下进行懒惰检查会带来相当深远的影响。

在示例之后

class foo{
    other* bar;
    ...
};
class other{
    ...
}

…其中(1)使用了未声明的名称,(2)缺少关键的分号,您可以询问

"那为什么不可能呢?(标准明确禁止这样做吗?为了什么?)

神圣标准要求每个名字在使用前都必须声明。

注意,在给定的C++实现中,即使是数据指针也可以具有不同的大小(void*保证能够容纳任何数据指针),更不用说函数指针了。

在首次使用之前声明需求的另一种选择是采用默认值,就像早期C时代的隐式int一样。例如,编译器可以假设other是一个类类型。但总的来说,这并没有多大帮助,而且会使语言复杂化。

然而,在这种特殊情况下,可以将other的必要最小声明与其第一次使用结合起来:

class foo{
    class other* bar;
    //...
};
class other{
    //...
};

或者,您可以使用普通的远期声明:

class other;
class foo{
    other* bar;
    //...
};
class other{
    //...
};

只是为了让编译器知道other是一个类类型,而不是例如函数,或者charvoid的同义词(如果基本原始数据指针的大小有任何变化,则指向这些类型的指针是最大的)。


然后断言:

"我上面发布的这个伪例子似乎没有什么实际意义。我之所以不想使用前向声明,是因为当您获得嵌套类时,没有可以保留封装的前向声明。

这是错误的。

例如,这很好用:

class Foo
{
private:
    class Other;
    Other* bar;
    class Other{};
public:
    Foo(): bar( new Other ) {}
};

但是,第一次使用时带有声明的快捷方式class Other* bar;在这种情况下不起作用。这是因为它实际上会将Other声明为命名空间级别的类,而不是嵌套类。

相关文章: