Reputation: 878
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
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