2

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?

Tom de Geus
  • 5,625
  • 2
  • 33
  • 77
  • Possible duplicate of [When can I use a forward declaration?](https://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration) – Rosme May 30 '17 at 15:03
  • 3
    Declare B before A!? – Borgleader May 30 '17 at 15:03
  • @Borgleader. Yes of course... But I want to do the same thing vice versa, i.e. use `A` in `B` – Tom de Geus May 30 '17 at 15:04
  • 2
    @TomdeGeus That requirement is not present in your question... Only A uses B in it. – Borgleader May 30 '17 at 15:05
  • @Borgleader You are right, I should have addressed this more clearly. I have edited the question. – Tom de Geus May 30 '17 at 15:09
  • Why don't you update the code instead of amending the question? This should be rather trivial. In doing so you can also strip the irrelevant parts (e.g. member function `A::A(int inp)` does not contribute to the issue). – IInspectable May 30 '17 at 15:25

3 Answers3

4

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.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
4

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
    
Walter
  • 44,150
  • 20
  • 113
  • 196
  • 2
    if you don't add `inline` to the definition as mentioned in the other answer you'd get linker errors if the header was included in multiple translation units. – PeterT May 30 '17 at 15:18
1

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.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185