Способ изменить параметр виртуального метода не создавая копию переменной

Рейтинг: 1Ответов: 2Опубликовано: 19.07.2023

Есть следующий упрощенный код:

class RHS
{
public:
    virtual ~RHS() {}

    virtual void operator()(const std::vector<double>& x, std::vector<double>& f, const double t) = 0;
};


class MyRHS : public RHS
{
public:
    void operator()(const std::vector<double>& x, std::vector<double>& f, const double t) override
    {
        // вычисляем f
    }
};


auto main(int, char* []) -> int
{
    MyRHS rhs{};
    std::vector<double> f(10, 0);
    std::vector<double> x(10, 0);

    std::vector<double> x_changed{x};
    x_changed[4] += 0.1;

    rhs(f, x_changed, 1.0);

    return 0;
}

Задача стоит - избавиться от создания копии x. Есть идея создать view-обёртку, например:

    template<typename state>
    class Variation
    {
        using state_t = state;
        using value_t = typename state_t::value_type;

        const state_t& x_;
        const size_t i_{};
        const value_t δ_;

    public:
        Variation(const state_t& x, const size_t i, const value_t δ)
            : x_(x), i_(i), δ_(δ) {}

        const value_t operator[](size_t idx) const
        {
            if (i_ == idx)
                return x_[idx] + δ_;
            else
                return x_[idx];
        }
    };

Но как передать её в RHS, не изменяя класс RHS. Может есть еще какие то способы избавиться от копирования в данном случае, не затрагивая сам класс RHS?

Ответы

▲ 3

Комментарий, но не помещающийся в комментарий...

std::vector<double> x(10, 0);

x += 0.1;
rhs(f, x, 1.0);
x -= 0.1;

Сам вектор же функцией не изменяется :)

▲ -1

Если вы пишите свой класс MyClass и вам нельзя менять шапку базового оператора, причем, вы не хотите тратить память на копию вектора, можете сделать так:

class MyRHS : public RHS
{ 
std::map<int, double> prepMap;
public:
void prepare(const int indx, const int val){
    prepMap[indx]=val;
}
public:
    void operator()(const std::vector<double>& x, std::vector<double>& f, const double t) override
    {
        for (auto i : prepMap)
            f[i.first]+=i.second;
        // вычисляем f
    }
};

Затем вызов:

rhs.prepare(4,0.1);
rhs(f, x, 1.0);

Да, согласна, что x, всё же будет изменен, так как немного не дописала оператор. Вношу изменения в оператор:

    void operator()(const std::vector<double>& x, std::vector<double>& f, const double t) override
    {
        for (auto i : prepMap)
           f[i.first]+=i.second;
        // вычисляем f

        for (auto i : prepMap){
              f[i.first]-=i.second;// меняем x обратно
              prepMap.erase(i); // очистка словаря
         }
    }

Реализация похожа на ответ выше, но при этом, юзеру кода не нужно заботиться о том, чтобы потом вычесть именно 0.1, а не 0.5. В большой простыне кода можно сделать ошибки при вычитании. Насчёт траты памяти не согласна, например: Вектор x содержит 1 000 000 элементов. Нужно поменять 3 (10й, 5000ный и 9000ый):

rhs.prepare(10,0.1);
rhs.prepare(5000,0.2);
rhs.prepare(9000,0.3);
rhs(f, x, 1.0);

Получается словарь из 3х элементов.