Questionable
Questionable

Reputation: 878

C++ Mutual header inclusion and forward declarations

How do I allow two classes mutually include one another so they can convert from one to the other.

Car.hpp

#ifndef CAR_HPP
#define CAR_HPP

#include "Truck.hpp"
class Car
{
public:
    Car(int weight) : weight(weight) {}
    Car(Truck data) : weight(ConvertFromTruck(data)) {}

private:
    int weight;
    int ConvertFromTruck(Truck data)
    {
        ... //in real life there would be a lot more to transfer than just weight.
    }
}
#endif //CAR_HPP

Truck.hpp

#ifndef TRUCK_HPP
#define TRUCK_HPP

#include "Car.hpp" //Obviously won't be included because of the CAR_HPP include guard
class Truck
{
public:
    Truck(int weight) : weight(weight) {}
    Truck(Car data) : weight(ConvertFromCar(data)) {}

private:
    int weight;
    int ConvertFromCar(Car data)
    {
        ...//in real life there would be a lot more than just weight
    }
}
#endif //TRUCK_HPP

Main.cpp

#include "Car.hpp"
#include "Truck.hpp"

int main()
{
    Car newCar(42);
    Truck newTruck(newCar);

    return 0;
}

So obviously Truck.hpp can't truly include Car.hpp becuase CAR_HPP is already defined. Also, Truck.hpp can not forward declare class Car; because Truck(Car data)... requires a complete type, and a forward declared class is not a complete type.

It looks like this is similar: Forward declaration being ignored? but there is no answer.

This topic states not to have mutual including headers. Forward Declarations and Includes

I would try to avoid this but how can I achieve a Car that can receive a Truck and convert it properly and a Truck that can receive a Car and convert it properly?

Is there a way I can use: operator Car() { ... } and operator Truck() { ... } so that a Car can be casted into a Truck and vice versa?

Upvotes: 2

Views: 1002

Answers (1)

Bathsheba
Bathsheba

Reputation: 234635

In the declaration

int ConvertFromTruck(Truck data)

Truck needs to be a complete type, which means that the class definition for Truck must be available to the compiler. And there lies your problem.

Fortunately there is a solution: pass the Truck by const reference:

int ConvertFromTruck(const Truck& data)

Here the compiler only requires an incomplete type for Truck, and a forward class declaration rather than the #include will suffice for that. This is very much superior at run-time too as you are not taking a value copy of the Truck when the function runs (although a compiler might optimise out that copy).

Do the same for the constructor (i.e. Car(const Truck& data)), and for the Truck class as well.

Note that I use a const reference as opposed to a non-const reference for two reasons (i) you don't want to be able to modify the object passed, and (ii) an anonymous temporary can bind to a const reference.

Upvotes: 5

Related Questions