Search code examples
c++classtemplatescallfunction-call

Pass function call from a class to one of its members based on another member parameter


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?

}


Solution

  • 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.