简体   繁体   中英

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 ?

}

std::variant might be what you need. Create a buffer class and forward all the container operations to it. There is no need to change too much in your class A , but you should mimic std::vector<T> in buffer , i implemented size() and const subscript operator . Here is the basic demonstration.

#include <variant>
#include <vector>
#include <iostream>

struct store_as_int {};
struct store_as_double {};

class buffer {
    public:
        buffer( store_as_int ) : data { std::vector<int>{} } {
            std::cout << "I am storing numbers as int" << std::endl;
        }
        buffer( store_as_double ) : data { std::vector<double>{} } {
            std::cout << "I am storing numbers as double" << std::endl;
        }

        [[nodiscard]] std::size_t size() const noexcept {
            std::size_t s;
            std::visit( [ &s ]( auto&& arg ) {
               s = arg.size(); 
            } , data );

            return s;
        }

        [[nodiscard]] double operator[]( std::size_t idx ) const {
            double s;
            std::visit( [ &s , idx ]( auto&& arg ) {
               s = arg[ idx ]; 
            } , data );

            return s;
        }

    private:
        std::variant< std::vector<int> , std::vector<double> > data;
};

class A{
    buffer a;
public:
    A() : a { store_as_int{} } {}
    A( store_as_double ) : a { store_as_double {} } {   }
    int getSize() const { return a.size(); }
    int getVal(int i) const { return a[i]; }
};

int main()
{
    A x;
    A y { store_as_double{} };
    int n = x.getSize();
    int t = x.getSize();

    std::cout << n << std::endl;
    std::cout << t << std::endl;
}

Output :

I am storing numbers as int
I am storing numbers as double
0
0

run online

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM