8

Why is this a compilation error:

void f() {
    g();
}

void g() {

}

but not this:

class X {
    void a() {
        b();
    }

    void b() {
        
    }
};

I was under the assumption the compiler would (at least, generally) read code from top-down, left-to-right, and that was the reason that in the 1st piece of code we'd need to define a void g() forward declaration before f() to make the code compile. But that logic doesn't seem to apply to classes -- why?

Boann
  • 48,794
  • 16
  • 117
  • 146
devoured elysium
  • 101,373
  • 131
  • 340
  • 557
  • 4
    Function body is a complete-class context and lookups from complete-class context are performed from immediately after the closing bracket `}` of the class specifier. – Language Lawyer Dec 28 '21 at 12:53
  • We can look at it this way (less formal): A function body can appear either out-of-line after the class declaration, or inline within the class. However, the language is designed so that the visibility of other members is the same regardless. – BoP Dec 28 '21 at 13:59

2 Answers2

3

C dates from 1972; C++ dates from 1985. C++ inherited the forward declaration requirement and the design of header files from C, and kept that behavior for compatibility, but was able to improve things for classes, with C didn't have.

Boann
  • 48,794
  • 16
  • 117
  • 146
  • 2
    Forward lookup [severely complicates](http://open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#2335) C++-specific features like overload resolution and deduced return types (and would do so more if unbounded); it’s not a clean improvement that was foregone for C compatibility. – Davis Herring Dec 30 '21 at 15:29
1

Since all members of a class must be declared within it, it is assumed by the language that a class has one authorship (which might comprise many individual authors). As such, it is reasonable to provide the convenience of forward lookup where technically feasible.

This also obviates the need for forward declarations of member functions and data members, which are accordingly disallowed.

The same argument does not apply to the open set of non-member functions, especially in the global namespace; it would be problematic for lookup to consider functions introduced by unrelated components later in the translation unit, especially if a declaration is available before the use but a later one might override it due to namespace search order or a better parameter match. That might outdate the return type of a function declared with auto after it had been used, for instance. It could also make it unclear even outside a template whether a name referred to a type or template, bringing in all the complexity of the typename and template parser guides.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • Is it "authorship" or simply tight lexical scope? (I.e., they _must_ be enclosed by the braces for the class decl.) Frankly, I think "authorship" is off base ... – davidbak Dec 28 '21 at 21:36
  • @davidbak: It can’t literally be size: the components of even a class that is longer than two header files are still taken to be more closely related than the contents of those headers (as judged by access control and instantiation, not just lookup). You can try to define some “conceptual size” for a lexical scope, or appeal to the closed nature of classes, but that ends up seeming awfully similar to (expected) authorship. – Davis Herring Dec 28 '21 at 21:44
  • Aren't we talking about method declarations - and definitions - within a _class declaration_? Must be enclosed by braces?? C++, not C#? I don't get your comment at all. – davidbak Dec 28 '21 at 21:47
  • 1
    @davidbak: Of course there are braces around (most of) the class definition, but the same applies to namespace definitions or linkage declarations, which do not have this behavior, so that doesn’t seem to be the important part. – Davis Herring Dec 28 '21 at 21:58
  • There can only be **one** class _declaration_ braced-block, unlike namespace or linkage declarations. **One.** That seems significant. That seems _local_. – davidbak Dec 28 '21 at 22:09
  • @davidbak: Obviously there can be only one. That’s the basis for my argument: that no member can be added after that *one* location. Committee discussions regularly refer to the presumption of unity of intent of a class (*e.g.*, with regard to defaulted `operator<=>`), which is the rest of it. – Davis Herring Dec 28 '21 at 22:19
  • But what does this have to do with "authorship"? It's strictly syntactical, and can be understood that way. – davidbak Dec 28 '21 at 22:26
  • @davidbak: The syntax is enough to describe the rules (by definition), but it cannot by itself explain the reasons for them. Any one namespace definition could have the same rule applied if the syntax were all that mattered; it would be inconsistent to treat the global namespace differently, but again that’s not a syntactic basis. – Davis Herring Dec 29 '21 at 01:28