Crook
Crook

Reputation: 833

Why do I receive a syntax error during compilation?

Could someone explain why I receive a syntax error: Identifier 'Bar' during building without the inclusion of class Bar; in the foo.hpp header file?

I don't receive any errors in Visual Studio 2019 before building, and the build order appears to be bar, then foo, then main, so following the #include statements it would seem as though the bar header file is first included inside the foo header during the build.

I have included code below outlining the basic problem.

//Foo header file
#pragma once

#include "bar.hpp"  
#include <iostream>

class Bar; //Commenting this line out results in no longer being able to build the project

class Foo {

public:
    Foo();

    void pickSomething(Bar& bar);
};

//Foo cpp file
#include "foo.hpp"

Foo::Foo() {
    std::cout << "Made Foo" << std::endl;
}

void Foo::pickSomething(Bar& bar) {
    bar.getSomething();
    std::cout << "Picked something!" << std::endl;
}

//Bar header file
#pragma once

#include "foo.hpp"
#include <iostream>

class Foo;

class Bar {

public:
    Bar(Foo& foo);

    void getSomething();
};

//Bar cpp file
#include "bar.hpp"

Bar::Bar(Foo& foo) {
    std::cout << "Made bar" << std::endl;
}

void Bar::getSomething() {
    std::cout << "Gave something!" << std::endl;
}

//main file
#include "foo.hpp"
#include "bar.hpp"

int main() {
    Foo foo;
    Bar bar(foo);
    foo.pickSomething(bar);

    return 0;
}

Upvotes: 1

Views: 638

Answers (2)

Anubis
Anubis

Reputation: 7445

Summary:

  • foo.hpp includes bar.hpp and bar.hpp includes foo.hpp. This is a cyclic dependency.
  • #pragma once makes sure to not to load the same header again, if it is already loaded.

  • Only the compilation of bar.cpp fails (foo.cpp and main.cpp will be successfully compiled)

Let's follow the pre-processor when compiling bar.cpp. Things happen in the following order.

  1. bar.cpp includes bar.hpp
  2. bar.hpp includes foo.hpp. (Note the following two points)
    • Pre-processor remembers it entered bar.hpp and will avoid entering it again in current cycle.
    • The symbol Bar (declaration of class Bar) is not still loaded as foo.hpp is included before that
  3. foo.hpp try to include bar.hpp
    • Pre-processor knows it already entered bar.hpp, hence this is ignored !
    • However, Foo class declaration uses a symbol called Bar. But this is not declared before !

The unknown symbol Bar could have represented many things (a class, a macro etc..). So the compiler (rightfully) fails as it doesn't know how to treat it.

The solution is forward declaration. There, you are guaranteeing the compiler that the symbol Bar is representing a class. As long as you are not doing anything where the compiler need to know about the class layout (e.g. define a member function of type Bar, access members of Bar etc...) the compilation will succeed.

Upvotes: 1

John Zwinck
John Zwinck

Reputation: 249462

foo.hpp and bar.hpp do not depend on each other, so forward declarations are a good way to avoid a circular dependency between their header files. Only the .cpp files actually need to include both header files.

Upvotes: 0

Related Questions