3

I have always been under the impression that C/C++ is parsed in 1 pass, therefore, a symbol must be declared before it can be used.

Thus this does not work:

void a()
{
   b();
}

void b() 
{
}

unless we forward declare void a();

However, I noticed that this does work:

class Foo 
{
  public:
  void a() 
  {
    b();
  }

  void b()
  {
  }
};

Why does this work? If C++ is parsed in a single pass through the code then this should not work I would think because the symbol Foo::b() has not been defined when it is called.

jmasterx
  • 52,639
  • 96
  • 311
  • 557
  • Chose the language first. Why do you think C++ is parsed in one pass? – Ed Heal Mar 22 '15 at 15:41
  • C++ is single pass per *source file*. Inside the file it can go back and forwards as much as it wants, although many things in the standard require to be declared first before they can be used. – Neil Kirk Mar 22 '15 at 15:41
  • @NeilKirk - Do you have evidence for this? – Ed Heal Mar 22 '15 at 15:42
  • @EdHeal Yes, the OP's code. – Neil Kirk Mar 22 '15 at 15:42
  • @NeilKirk - You edited your comment after mine – Ed Heal Mar 22 '15 at 15:43
  • Member function bodies are always in the scope of the entire class. So are static data member definitions. – David G Mar 22 '15 at 15:47
  • "So are static data member definitions"... well, that's trivially true. Static data member definitions always come after the closing brace -- there is no such thing as an inline definition of a static data member. – Ben Voigt Mar 22 '15 at 17:02

4 Answers4

1

There are things called forward references (different from forward declaration)

class C
{
public:
   void mutator(int x) { myValue = x; }
   int accessor() const { return myValue; }
private:
   int myValue;
};

here myValue is accessed before it it declared. C++ does not usually allow forward references but for class members it allows them. It is the compilers job to remember the definition of mutator and accessor until it sees myValue

Jesse Laning
  • 490
  • 4
  • 12
  • You have it backwards -- forward reference is allowed not just because you are referencing a class member, but because the reference appears inside a function body. For example, this forward reference to a class member is NOT legal: `void mutator(decltype(this->accessor()) x); int accessor() const;` – Ben Voigt Mar 22 '15 at 16:56
1

The definition of your class:

class Foo 
{
public:
  void a()  {  b(); }
  void b()  {  }
};

has the same meaning than:

class Foo 
{
public:
  void a();
  void b();
};
void Foo::a()  {  b(); }
void Foo::b()  {  }

This is why the function body sees all the members, as if the class was already completely defined. This is by the ways stated in the C++ standard:

3.3.7/1 The potential scope of a name declared in a class consists not only of the declarative region following the name’s point of declaration, but also of all function bodies, brace-or-equal-initializers of non-static data members, and default arguments in that class (including such things in nested classes).

The compiler still parses the file in a single pass. But the parsing the grammatical construct is only a part of the larger compilation process, in which applying the context to the parsed grammar production plays also a role (see also this SO question).

Community
  • 1
  • 1
Christophe
  • 68,716
  • 7
  • 72
  • 138
0

On the first pass all the class interface is read and thus when the code for function a is compiled it knows that the function b exists in the class Foo

in need of help
  • 1,606
  • 14
  • 27
0

Section 3.3.7 of the C++ Standard includes, among other rules, the statement that

The potential scope of a name declared in a class consists not only of the declarative region following the name’s point of declaration, but also of all function bodies, default arguments, exception-specifications, and brace-or-equal-initializers of non-static data members in that class (including such things in nested classes).

Basically, this requires that analysis of the mentioned contexts, if they appear inline inside the class definition, is deferred until the entire class definition has been processed just as if you had used an out-of-line definition.

A related rule is found in section 9.2:

A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, using-declarations introducing inheriting constructors (12.9), exception-specifications, and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720