为什么是“使用前声明”?类中不需要规则吗?

发布于 2024-09-29 13:03:51 字数 383 浏览 6 评论 0原文

我想知道为什么 C++ 的“使用前声明”规则在类中不成立。

看这个例子:

#ifdef BASE
struct Base {
#endif
    struct B;
    struct A {
        B *b;
        A(){ b->foo(); }
    };

    struct B {
        void foo() {}
    };
#ifdef BASE
};
#endif

int main( ) { return 0; }

如果定义了 BASE,则代码有效。

在 A 的构造函数中,我可以使用尚未声明的 B::foo。

为什么这会起作用,而且最重要的是,为什么只在类中起作用?

I'm wondering why the declare-before-use rule of C++ doesn't hold inside a class.

Look at this example:

#ifdef BASE
struct Base {
#endif
    struct B;
    struct A {
        B *b;
        A(){ b->foo(); }
    };

    struct B {
        void foo() {}
    };
#ifdef BASE
};
#endif

int main( ) { return 0; }

If BASE is defined, the code is valid.

Within A's constructor I can use B::foo, which hasn't been declared yet.

Why does this work and, mostly, why only works inside a class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

£噩梦荏苒 2024-10-06 13:03:51

好吧,说句迂腐的话,C++ 中没有“使用前声明”规则。名称查找的规则非常复杂,但可以(并且通常)粗略地简化为通用的“使用前声明规则”,但有一些例外。 (在某种程度上,这种情况类似于“运算符优先级和结合性”规则。虽然语言规范没有这样的概念,但我们在实践中经常使用它们,尽管它们并不完全准确。)

这实际上是这些例外之一。 C++ 中的成员函数定义被明确且有意地排除在“使用前声明”规则之外,从某种意义上说,从这些成员的主体中执行名称查找就像它们是在类定义之后定义的一样。

语言规范在 3.4.1/8(和脚注 30)中指出了这一点,尽管它使用了不同的措辞。它表示,在从成员函数定义进行名称查找期间,将检查整个类定义,而不仅仅是成员函数定义上方的部分。脚注 30 还指出,对于类定义内部或类定义外部定义的函数,查找规则是相同的(这与我上面所说的差不多)。

你的例子有点不简单。它提出了关于嵌套类中的成员函数定义的直接问题:它们是否应该被解释为好像它们是在最封闭的类的定义之后定义的?答案是肯定的。 3.4.1/8 也涵盖了这种情况。

《C++ 的设计与演化》一书描述了这些决定背后的推理。

Well, to be pedantic there's no "declare before use rule" in C++. There are rules of name lookup, which are pretty complicated, but which can be (and often are) roughly simplified into the generic "declare before use rule" with a number of exceptions. (In a way, the situation is similar to "operator precedence and associativity" rules. While the language specification has no such concepts, we often use them in practice, even though they are not entirely accurate.)

This is actually one of those exceptions. Member function definitions in C++ are specifically and intentionally excluded from that "declare before use rule" in a sense that name lookup from the bodies of these members is performed as if they are defined after the class definition.

The language specification states that in 3.4.1/8 (and footnote 30), although it uses a different wording. It says that during the name lookup from the member function definition, the entire class definition is inspected, not just the portion above the member function definition. Footnote 30 additionally states though that the lookup rules are the same for functions defined inside the class definition or outside the class definition (which is pretty much what I said above).

Your example is a bit non-trivial. It raises the immediate question about member function definitions in nested classes: should they be interpreted as if they are defined after the definition of the most enclosing class? The answer is yes. 3.4.1/8 covers this situation as well.

"Design & Evolution of C++" book describes the reasoning behind these decisions.

じ违心 2024-10-06 13:03:51

这是因为成员函数仅在编译器解析整个类定义后才进行编译,即使函数定义是内联编写的,而常规函数在读取后立即编译。 C++ 标准要求这种行为。

That's because member functions are compiled only after the whole class definition has been parsed by the compiler, even when the function definition is written inline, whereas regular functions are compiled immediatedly after being read. The C++ standard requires this behaviour.

秋千易 2024-10-06 13:03:51

我不知道该标准的章节和章节。

但是,如果您严格在类中应用“使用前声明”规则,则也无法在类声明的底部声明成员变量。您必须首先声明它们,以便在构造函数初始化列表中使用它们。

我可以想象“使用前声明”规则在类声明中已经放宽了一点,以允许“更干净”的整体布局。

正如我所说,只是猜测。

I don't know the chapter and verse of the standard on this.

But if you would apply the "declare before use" rule strictly within a class, you would not be able to declare member variables at the bottom of the class declaration either. You would have to declare them first, in order to use them e.g. in a constructor initialization list.

I could imagine the "declare before use" rule has been relaxed a bit within the class declaration to allow for "cleaner" overall layout.

Just guesswork, as I said.

灼痛 2024-10-06 13:03:51

C++ 定义中最棘手的问题与名称查找有关:到底名称的哪些用途引用哪些声明?在这里,我将仅描述一种查找问题:与类成员声明之间的顺序依赖关系相关的问题。 [...]

由于目标之间的冲突而出现困难:

  1. 我们希望能够仅读取源文本一次即可进行语法分析。
  2. 对类的成员重新排序不应改变该类的含义。
  3. 显式内联编写的成员函数体在外联编写时应该具有相同的含义。
  4. 外部作用域中的名称应该可以在内部作用域中使用(与 C 中的方式相同)。
  5. 名称查找规则应独立于名称所指的内容。

如果所有这些都成立,则该语言的解析速度将相当快,并且用户不必担心这些规则,因为编译器会捕获不明确和接近不明确的情况。目前的规则非常接近这个理想。

[C++ 的设计和演变,第 138 页上的第 6.3.1 节,称为查找问题]

The most stubborn problems in the definition of C++ relate to name lookup: exactly which uses of a name refer to which declarations? Here, I'll describe just one kind of lookup problem: the ones that relate to order dependencies between class member declarations. [...]

Difficulties arise because of conflicts between goals:

  1. We want to be able to do syntax analysis reading the source text once only.
  2. Reordering the members of a class should not change the meaning of the class.
  3. A member function body explicitly written inline should mean the same thing when written out of line.
  4. Names from an outer scope should be usable from an inner scope (in the same way as they are in C).
  5. The rules for name lookup should be independent of what a name refers to.

If all of these hold, the language will be reasonably fast to parse, and users won't have to worry about these rules because the compiler will catch the ambiguous and near ambiguous cases. The current rules come very close to this ideal.

[The Design And Evolution Of C++, section 6.3.1 called Lookup Issues on page 138]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文