herophant
herophant

Reputation: 670

Why are forward declarations not working in this case?

Consider the following example:

a.h:

#ifndef A_H
#define A_H

#include <vector>
#include <memory>
#include "b.h"

enum class SActionType;

class A {
    std::vector<std::unique_ptr<B>> my_bees;
public:
    A();
    void print(int x, int y);
};
#endif

a.cc:

#include <iostream>
#include "a.h"

A::A() {
}

void A::print(int x, int y) {
    std::cout << "hi this is not going to work" << std::endl;
}

b.h:

#ifndef B_H
#define B_H

#include <memory>
#include <vector>
// #include "a.h"

class A;
class B {
    std::shared_ptr<A> a;
public:
    B(std::shared_ptr<A> a);
    void foo();
};
#endif

b.cc:

#include <algorithm>
#include "b.h"

B::B(std::shared_ptr<A> a):
    a{a}
{}

void B::foo() {
    a->print(1, 2);
}

main.cc:

#include "a.h"
#include "b.h"

int main() {
    return 0;
}

Makefile:

exec: main.o a.o b.o
    g++ -std=c++14 -o exec
main.o: main.cc
a.o: a.cc a.h
b.o: b.cc b.h

After running make, I get the following error:

me:~$ make
g++    -c -o b.o b.cc
b.cc: In member function ‘void B::foo()’:
b.cc:9:6: error: invalid use of incomplete type ‘using element_type = class A {aka class A}’
     a->print(1, 2);
      ^~
In file included from b.cc:2:0:
b.h:8:7: note: forward declaration of ‘using element_type = class A {aka class A}’
 class A;
       ^
<builtin>: recipe for target 'b.o' failed
make: *** [b.o] Error 1

Why is this happening? I thought that this would work because of class B does not actually need to access the member functions of A until the .cc files, and just having the forward declaration in the header file would be enough to circumvent the circular dependency.

Upvotes: 1

Views: 403

Answers (2)

HTNW
HTNW

Reputation: 29148

I thought that this would work because of class B does not actually need to access the member functions of A until the .cc files

That's correct. b.h does not need to include a.h (nor does a.h need to include b.h). But b.cc needs to include both. Nothing circular about that. After all, a.cc does the same thing.

b.cc
#include <algorithm>
#include "a.h"
#include "b.h"

B::B(std::shared_ptr<A> a):
    a{a}
{}

void B::foo() {
    a->print(1, 2);
}

Upvotes: 3

Vlad from Moscow
Vlad from Moscow

Reputation: 310920

The compiler needs to know the complete type of the class A that to check for example that this construction

void B::foo() {
    a->print(1, 2);
}

is valid that is that the class A contains the member function (static or non-static?) print.

Upvotes: 0

Related Questions