Вы можете делать то, что хотите, с правильной теоретической операцией, которая НЕ является полиморфизмом, а объединением.Большинство людей понятия не имеют, что такое тип суммы (дискриминационный союз) и для чего он нужен, поэтому они постоянно злоупотребляют наследованием, которое совершенно не связано.
Объединения более популярны в C, например, сообщения о событиях в X-Windows основаны на объединениях, потому что они разбиты в C ++.
Объединение - это правильный путьпредставлять гетерогенные типы данных как один тип, отсюда и объединение имен: оно объединяет все компоненты в один тип.У союзов всегда есть конечное известное число компонентов, функции, использующие союзы, неизменно используют переключатель на дискриминанте для выбора правильного кода обработчика.
ООП не может обеспечить объединение: оно предоставляет подтипы.
Шаблоны предоставляютопять нечто совершенно иное: параметрический полиморфизм.
Все три понятия совершенно различны как в теории, так и на практике.Стиль ООП для подтипов оказывается наименее полезным , потому что то, что он может представлять, сильно ограничен, однако эти ограничения допускают динамическую диспетчеризацию для открытого набора подтипов, что очень хорошо , если оно применяется к вашей проблеме.
Итак, теперь ясно, что в вашем массиве все, что вам нужно поместить, это объединение всех ваших классов, и ваша проблема исчезнет.
Только ...классы должны быть POD-типами в C ++ в настоящее время из-за беспринципного ограничения.Поэтому лучшим решением будет использование объединения необработанных функций C, поскольку указатели на функции C являются POD.
Что-то вроде:
enum tag {tag_X, tag_Y};
struct X { int x; };
void px(X a) { cout << a.x; }
struct PX { tag t; X a; void (*p)(X); };
struct Y { double x; };
void py(Y a) { cout << a.x; };
struct PY {tag t; Y a; void (*p)(Y); };
union XY { PX anX; PY anY; };
PX x1 = { tag_X, { 1 }, px };
PY y1 = { tag_Y, { 1.0 }, py };
XY xy1.anPX = x1;
XY xy2.anPy = x2;
XY xys[2] = {xy1, xy1};
xy = xys[0];
switch (xy.anPX.tag) { // hack
case tag_X: xy.anPX.p (xy.PX.x); break;
case tag_Y: xy.anPY.p (xy.PY.x); break;
}
Если вы считаете, что это некрасиво, вы правы: C и C ++ умерли.Другое решение состоит в том, чтобы использовать тег и указатель, который приведен к void *, а затем использовать тег для приведения к требуемому типу: это намного проще, но требует выделения данных в куче, и, следовательно, у вас есть проблема с управлением памятью.Другой альтернативой является вариант типа Boost (который автоматизирует некоторые операции по уборке, но все еще очень уродлив).
Вот аналогичный код в Ocaml:
type xy = X of int | Y of double
let print u =
match u with
| X x -> print_int x
| Y x -> print_double x
in
print (X 1);
print (Y 2.0)
В этом коде X и Y - этотеги кода C выше, они называются конструкторами типов, потому что они создают тип xy из целого или двойного (соответственно).В выражении соответствия есть просто переключатель с автоматическим выбором правильного типа компонента и областью видимости, который используется для гарантии того, что вы не можете ссылаться на неправильный компонент (как вы могли бы в коде C), также нет прерывания, обработчики соответствия неDrop Thru, и управление памятью осуществляется сборщиком мусора.