Tom de Geus
Tom de Geus

Reputation: 5955

Header only library: use class defined later on in the header

I am creating a header-only library. In this library I would like to already use a class that is defined later in the header. For example:

class A {
public:
  int i;

  A(int x) : i(x) {};

  B func(B inp) {
    B out(i*inp.j);
    return out;
  };
};

class B {
public:
  int j;

  B(int x) : j(x) {};

  A other(A inp) {
    A out(j*inp.i);
    return out;
  };
};

Naturally my compiler does not like this. It produces the first error on the line on which func is defined:

 error: unknown type name 'B'

Is there a way to overcome this?

Upvotes: 2

Views: 353

Answers (3)

Walter
Walter

Reputation: 45414

Using a reference or pointer requires a declaration of the corresponding type. Using an object (instance) or any member requires the full definition or the corresponding type. A declaration is

class A;   // declares: there is a class called 'A'

while a definition is

class A {  // (re-)declares A and defines its layout and members
   /* details */
};

A declaration can precede a definition (but does not have to for the definition to be valid). This immediately gives the following solution.

  1. Order the class definitions such that later defined classes only use objects and members of earlier defined class in their interface. For example

    class A {
      int i;
    public:
      A(int x) : i(x) {}
    };
    
    class B {
      A a;                // object of type A: okay, since A is defined
    public:
      B(int i) : a(i) {}
      int foo() const {
        return a.i;       // member of a: okay, since A::i is defined
      }
    };
    
  2. Classes of which references or pointers are used in any earlier declared class can be forward declared (not defined) before any class definition. For example

    class B;              // forward declaration of B
    
    class A {
      int i;
    public:
      A(int x) : i(x) {}
      A(B*);              // pointer to B okay, since B is declared
                          // cannot define A::A(B*): it depends on definition of B
    };
    
    class B {
      int x;
      A a;                // object of type A: okay, since A is defined
    public:
      B(int i) : x(i+i), a(i) {}
      B() : a(this);
    }
    
  3. Define any implementation details that depend on the definition of classes not known at the time of declaration.

    inline A::A(B*b)      // either inline or in a source file
      : i(b->x/2)         // using member B::x okay: B is defined
    

Upvotes: 4

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122133

Not saying that this would be the way to go, but if a little misuse of templates is fine with you, you could do something like this (same example but removed irrelevant stuff and added B using A as you mentioned):

template <typename X>
struct A {
  X func(X inp) { return X(/*...*/); }
};

struct B {
public:
    A<B> func(A<B> inp) { return A<B>(); }
};

As I said, I wouldnt really do this just to avoid a forward declaration. On the other hand, you should reconsider if you really need the circular dependency. If two classes depend too much on each other, you cant touch one without having to check if the other didnt break. Thats not nice and if possible I would avoid it.

Upvotes: 1

Some programmer dude
Some programmer dude

Reputation: 409136

One way is to forward declare the class B, and move the function definition out of the class.

Normally one would put the definition in a source file, but since it's a header-only library you can instead mark the function definition as inline and you can put it inside the header file itself, but after the definition of the B class of course.

Another and more simpler way, if B doesn't depend on A, is to move the definition of B to be above the definition of A in the header file.

Upvotes: 4

Related Questions