Обходной путь для функциональности виртуальной статической функции - PullRequest
0 голосов
/ 21 января 2019

Допустим, у меня есть класс:

class StateVector {
protected:
    float* _v;

public:

    StateVector():_v(new float[size()]) {}
    virtual ~StateVector() { delete [] _v; }

    virtual size_t size() = 0;

    // ...

};

class PositionState : public StateVector {
public:
    size_t size() { return 3; }
    float* x()    { return _v; }
};

class MovingState : public PositionState {
public:
    size_t size() { return PositionState::size() + 3; }
    float* v()    { return _v + PositionState::size(); }
};

Цель здесь - разрешить производным классам указывать размер вектора состояния путем переопределения size().(Эта информация должна быть доступна для конструктора StateVector, базового класса, которому принадлежит базовый массив).

Однако это не совсем идеально по нескольким причинам:

  • Во-первых, в этой реализации size() должен / будет одинаковым для всех экземпляров класса.Но в этом факторинге нет ничего, что мешало бы различным экземплярам одного и того же класса иметь разные мнения о size().

  • Во-вторых, другим классам необходимо генерировать экземпляр для запросасоответствующий размер:

    template <typename State>
    class StateTransition {
    
        Matrix<float> _m;
    
        // constructor for Matrix takes #rows, #cols
        StateTransition():_m(State().size(), State().size()) {}
    
        // ...
    
    };
    

Это глупо, потому что для всех State s size () будет одинаковым.В этом случае size () может быть довольно большим, и в конструкторе для StateTransition (построив два State s) выделит два массива этого размера, а затем сразу же выбросит их!

НаконецОжидается, что каждый производный класс будет нести расширенный набор своего базового состояния, поэтому size () никогда не должен быть меньше для базовых классов, чем для производных классов - но, поскольку мы не можем пройти по дереву наследования, я не знаюспособ обеспечить это программно.Это второстепенная проблема, но было бы неплохо, если бы существовал чистый способ справиться с этим.

Было бы наиболее разумно иметь возможность написать:

class StateVector {
    float* _v;
    StateVector:_v(new float[size()]) {}

    virtual static size_t size() = 0;
};

class PositionState {
    static size_t size() { return 3; }
    // ...
};

// etc.

template <typename State>
class StateTransition {

    Matrix<float> _m;

    StateTransition():_m(State::size(), State::size()) {}
};

Однако,другие ответы здесь (и в других местах) указывают, что виртуальные статические функции недопустимы (и некоторые из них бесполезно предполагают, что это «не имеет смысла» или «не будет полезно»).

Что такое идиоматический способ решения этой проблемы, который позволяет производным классам легко следовать правилам?

1 Ответ

0 голосов
/ 21 января 2019

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

class StateVectorInfo {
public:
  virtual int size() const = 0;
protected:
  ~StateVectorInfo () = default;
};

class PositionStateInfo : public StateVectorInfo {
  PositionStateInfo (); // don't create other instances
public:
  virtual int size() const;
  static PositionStateInfo info; // single instance
};

PositionStateInfo PositionStateInfo::info; // a definition is needed

class StateVector {
    float* _v;
public:
    StateVector (const StateVectorInfo& info):
       _v(new float[info.size()]) {
    }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...