Assume I have the following class A
which is passed around through different function calls and wrappers.
class A{
std::vector<int> a;
public:
int getSize const {return a.size();}
int getVal(int i) const {return a[i];}
// other private and public members and functions
}
Now for some reason I need the same class but with a double vector. I cannot templatize this class, because there are many function signatures that I cannot change. What is suggested is to rename A
to A0
, templatize that, create new A
containing A0<int>
, and A0<double>
as follows:
template <typename T>
class A0{
std::vector<T> a;
public:
int getSize const {return a.size();}
T getVal(int i) const {return a[i];}
// other private and public members and functions
}
class A{
// only one of the following will be initialized in the constructor and the other one will be null.
std::shared_ptr<A0<int>> iA;
std::shared_ptr<A0<double>> dA;
// also the following flag will be set in the constructor
bool isInt;
}
This is the question: If I want to make minimum changes in different places of the code that previously accessed, changed, or just passed around the instances of class A
, what shall be done? For example, consider this in a different part of the old code:
A x;
int n = x.getSize();
Is there a way to keep that old code without implementing a method getSize()
inside the new A
class that would contain an if-conditional statement and return either iA->getSize()
or dA->getSize()
based on isInt
? Is there a smart way to do this?
Is there any other suggestions for achieving the goal of minimum modifications in different parts of the code that use (mostly pass around) the old A
?
}
If this is only about passing on the object (or a reference to it) and not actually using any member of the object, then you can simply use a std::variant
:
using A = std::variant<A0<int>, A0<double>>;
At the point where members are actually used, use std::visit
to determine the type and act on it.
If passing happens only by-reference, then making the A
an empty base class of A0<T>
will also work. You can then cast back to the real type using static_cast<A0<int>&>(...)
or static_cast<A0<double>&>(...)
to use the members at the destination. You must however assure that you cast to the actual type of the passed object, otherwise you have undefined behavior.
An alternative is dynamic_cast
instead of static_cast
, which will throw or return a null pointer if the types don't match. But that requires the base class A
to have a virtual method to be polymorphic.