Search code examples
constructordependenciesconstexprforward-declaration

Mutually dependent constexpr constructors


I have two classes, each constructible from the other.

Example:

class B;

class A{
 public:
  double val;
  constexpr A(B b): val(b.val){};
};

class B{
 public:
  double val;
  constexpr B(A a): val(a.val){};
};

I need to forward-declare class B, so A knows about it. When these constructors are not constexpr, I can move their definitions to a source file and it happily compiles.

However, to make it constexpr, they have to be defined in the header. B is ok to construct from A, because it sees the full definition of A. A cannot construct from B because it only sees a declaration, and therefore has no idea about B::val.

I'm left with only making class B constexpr. Is there a way to do it for both classes?


Solution

  • Using gcc I get the error (https://godbolt.org/z/qvP7absdr):

    <source>:6:15: error: 'b' has incomplete type
        6 | constexpr A(B b) : val(b.val){};
          |             ~~^
    <source>:1:7: note: forward declaration of 'class B'
        1 | class B;
          |       ^
    <source>: In constructor 'constexpr A::A(B)':
    <source>:6:11: error: invalid type for parameter 1 of 'constexpr' function 'constexpr A::A(B)'
        6 | constexpr A(B b) : val(b.val){};
          |           ^
    Compiler returned: 1
    

    So this fails because type B is incomplete when it is used it is the definition of the constructor A::A(B b).

    In order to deal with this we can wait until we have declared B fully before we define the constructor and use B. Essentially, move the definition of the constructor out of class A and after class B

    class B;
    
    class A{
     public:
      double val;
      constexpr A(B b);
    };
    
    class B{
     public:
      double val;
      constexpr B(A a): val(a.val){};
    };
    
    constexpr A::A(B b) : val(b.val){};
    

    See an example without compilations issues: https://godbolt.org/z/44fbcr8sh