-2

As per this SO answer, modern compilers just do a single pass. Assuming GCC to be a modern one, why does the following simple program give me an error saying that test() was not declared in this scope:

// Example program
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string str="Naggy";
    test(str);

    cout<<str<<"\n";
}

void test(string& str) {
    str="Rathore";
}

I know this error can be easily resolved by having a function prototype for test() above main(); or by declaring and defining test() before main(). However, I am not asking for a workaround - I am asking why does the compiler not detect that I have declared and defined test() at a later stage in the program?

From what I remember from having learnt in school, many assemblers parse the input code twice; but if GCC (and maybe its assembler?) does that only once, then why doesn't it take such forward references into account? Or, am I missing something?

Thanks.

Note: cpp.sh does use GCC as noted here.

Edit: As per C++ rules, we need to declare the stuff before we use it. If (and when) the assembler does pass 1, does it not find out (and make a note) of the stuff that has been declared (like test() in this case) and then use that later in pass 2 to to resolve the apparent forward references? Or, does this responsibility NOT lie with the assembler?

J. Doe
  • 1,291
  • 2
  • 10
  • 19
  • 1
    How can you tell `test` is defined when you first reach it if you haven't seen it and you are only doing a single pass? – NathanOliver Feb 06 '18 at 21:57
  • 1
    That's not a compiler issue. The C++ language says that you have to declare stuff before you use it. How many times the compiler reads your source is irrelevant here. – Mat Feb 06 '18 at 21:57
  • @NathanOliver, editing the question to answer that. – J. Doe Feb 06 '18 at 21:57
  • Possible duplicate of [How does the compilation/linking process work?](https://stackoverflow.com/questions/6264249/how-does-the-compilation-linking-process-work) –  Feb 06 '18 at 22:01
  • To add to the confusion [the C++ standard requires 2 passes](https://stackoverflow.com/questions/7767626/two-phase-lookup-explanation-needed) in certain situations. – nwp Feb 06 '18 at 22:05
  • @nwp: Not really. Two phase lookup doesn't require two passes. – MikeMB Feb 06 '18 at 22:28

2 Answers2

1

The reason it doesn't work is because it only does a single pass. To support a function defined after it's used, it would have to make two passes: Pass 1 finds all the function signatures, pass 2 checks the calls against all the signatures to determine if the function is defined.

The specification of C++ was written with this in mind, which is why it requires all functions and classes to be defined before they're referenced.

Assemblers are often able to handle forward references with a single pass because the language is simpler. When they encounter an unknown identifier, they don't need to know what its declaration is -- it's just an address. They can leave a placeholder in the output data, and when they get to the definition they can update the placeholder.

C++ is more complicated, because a name can refer to a function or a class, and if it's a function there can be multiple overloads. Parsing the use of the name depends on what it names, so it's not really feasible to leave a placeholder until the definition is found.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • So, if I get it right, the assembler makes two passes, while the compiler makes just a single pass. If this is right, then does the assembler not make a note of this when it makes two passes? – J. Doe Feb 06 '18 at 22:03
  • Sir, I agree with your answer.... Even though it is not directly said in standards document, it seems like C++ standard allows multipass compilers. Is my assumption correct? please look into below link and search for "multi-pass" ...... http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4713.pdf – Pavan Chandaka Feb 06 '18 at 22:07
  • 2
    @J.Doe Why do you put the same comment under every answer? And why do you suddenly talk about assemblers? They have nothing to do with anything. – nwp Feb 06 '18 at 22:08
  • @nwp, 1. because I have the same question to ask in each case (since their answers do not talk about it (at least in a way I understand)). And 2. assemblers, since I wonder why the assembler does not do it if it does two passes. – J. Doe Feb 06 '18 at 22:10
  • They're different languages, they have different rules. – Barmar Feb 06 '18 at 22:27
  • @J.Doe I've updated the answer to explain the difference between what can be done in C++ versus assembler. – Barmar Feb 06 '18 at 22:34
1

C++ language is much more complex than you think... for example there are cases in which this kind if "look ahead" is indeed performed:

struct Foo {
    void baz() {
        bar(); // ok, even if bar comes later
    }
    void bar() {
        printf("Hello, world.\n");
    }
};

The compiler is giving an error in your case because C++ rules say so. Many parts of C++ are logical, but many also are not logical at all.

Consider that C++ has even a rule so complex that requires the compiler to read an unlimited number of tokens before it can decide what is the semantic meaning of the very first of them (the dreaded "most vexing parse" rule).

With C++ the only safe approach sometimes to just learn the rules: don't try too hard to always find a logic, may be there's none.

6502
  • 112,025
  • 15
  • 165
  • 265
  • So, if I get it right, the assembler makes two passes, while the compiler makes just a single pass. If this is right, then does the assembler not make a note of this when it makes two passes? – J. Doe Feb 06 '18 at 22:05