Передача вызова функции из класса одному из его членов на основе другого параметра-члена - PullRequest
0 голосов
/ 26 февраля 2020

Предположим, у меня есть следующее class A, которое передается через различные вызовы функций и оболочки.

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 
}

Теперь по какой-то причине мне нужен тот же класс, но с двойным вектором. Я не могу шаблонизировать этот класс, потому что есть много сигнатур функций, которые я не могу изменить. Предлагается переименовать A в A0, шаблонизировать его, создать новые A, содержащие A0<int> и A0<double>, следующим образом:

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;
}

Это вопрос: если я хотите сделать минимальные изменения в разных местах кода, который ранее обращался, изменялся или просто передавал экземпляры класса A, что должно быть сделано? Например, рассмотрим это в другой части старого кода:

A x;
int n = x.getSize();

Есть ли способ сохранить этот старый код без реализации метода getSize() внутри нового класса A, который будет содержать условный оператор и возвращает либо iA->getSize(), либо dA->getSize() на основе isInt? Есть ли умный способ сделать это?

Есть ли другие предложения для достижения цели минимальных изменений в различных частях кода, которые используют (в основном обходят) старый A?

}

Ответы [ 2 ]

1 голос
/ 26 февраля 2020

Если речь идет только о передаче объекта (или ссылки на него), а не о фактическом использовании какого-либо члена объекта, тогда вы можете просто использовать std::variant:

using A = std::variant<A0<int>, A0<double>>;

В точку, где члены фактически используются, используйте std::visit, чтобы определить тип и действовать в соответствии с ним.


Если передача происходит только по ссылке, то A становится пустым базовым классом * 1010. * также будет работать. Затем вы можете привести к реальному типу, используя static_cast<A0<int>&>(...) или static_cast<A0<double>&>(...), чтобы использовать членов в месте назначения. Вы должны однако гарантируете, что вы приведете к фактическому типу переданного объекта, в противном случае у вас будет неопределенное поведение.

Альтернатива - dynamic_cast вместо static_cast, которая будет выбрасывать или вернуть нулевой указатель, если типы не совпадают. Но для этого требуется, чтобы базовый класс A имел виртуальный метод полиморфный c.

1 голос
/ 26 февраля 2020

std::variant может быть то, что вам нужно. Создайте класс buffer и направьте в него все контейнерные операции. Нет необходимости слишком сильно менять в своем классе A, но вы должны mimi c std::vector<T> в buffer, я реализовал size() и const subscript operator. Вот базовая c демонстрация.

#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;
}

Вывод:

Я храню числа как int
Я храню числа как двойные
0
0

запустить онлайн

...