Допустим, у меня есть класс:
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()) {}
};
Однако,другие ответы здесь (и в других местах) указывают, что виртуальные статические функции недопустимы (и некоторые из них бесполезно предполагают, что это «не имеет смысла» или «не будет полезно»).
Что такое идиоматический способ решения этой проблемы, который позволяет производным классам легко следовать правилам?