Поскольку мой противоречивый вопрос звучит сбивающим с толку, я думаю, что лучше четко указать, чего я хочу достичь.
У меня есть (пока не обращайте внимания на наследование и сосредоточьтесь на X):
class Base {};
class X : public Base {
private:
double m_double;
public:
template<class A> friend
void state( A& a, const X& x ) {
data( a, x.m_double, "m_double" );
}
};
Теперь я могу вводить произвольные новые классы, которые выполняют различные действия в зависимости от того, как перегружена функция данных, мы будем называть их Accessors в следующем:
class XmlArchive {...}; //One Accessor
template<class T>
void data( XmlArchive& a, const T& t, const std::string& str ) {
//read data and serialize it to xml Archive
}
class ParameterList {...}; //Another Accessor
template<class T>
void data( ParameterList& a, const T& t, const std::string& str ) {
//put data in a parameter list
}
Тогда я могу написать:
X myX;
XmlArchive myArchive;
state( myArchive, myX );
ParameterList myParameters;
state( myParameters, myX );
Фантастика, повторное использование кода! : D Однако следующее (явно) не удается:
Base* basePtr = new X; //This would come from factory really, I should not know the type X
state( myParameters, *basePtr ); //Error
Цель состоит в том, чтобы сделать этот последний вызов успешным. Что я учел (и почему это не приемлемо):
Первый вариант: заставить все Accessors наследовать от общего базового класса, скажем, AccessorBase, затем записать в Base
virtual state( AccessorBase& a ) const = 0;
и реализовать необходимый код в X (синтаксис для вызова состояния будет незначительно отличаться, но это можно исправить).
Проблема заключается в том, что AccessorBase должен иметь виртуальную функцию для каждого возможного типа, которая является вторым аргументом в функции (ах) данных. Поскольку эти типы могут быть определяемыми пользователем классами (см. Случай композиции классов, X, в которой Y является членом данных), я не понимаю, как заставить эту стратегию работать.
Второй вариант: создать виртуальную функцию в базе для каждого аксессора. Это нарушает принцип открытия / закрытия, так как добавление нового класса Accessor (скажем, TxtArchive) потребует модификации базового класса.
Я понимаю, почему виртуальная функция-член не может быть шаблонизирована (возможно, разные vtbls в разных единицах компиляции).
Однако мне кажется, что должно быть решение этой проблемы ... Бэйс знает, что это действительно тип X, и тип Accessor всегда является явным, так что это вопрос поиска способа вызова (для Аксессор типа XmlArchive):
state( xmlArchive, x ); //xmlArchive of type XmlArchive, x of type X
, который даст результат.
Подводя итог, я хотел бы позвонить:
state( myParameters, *basePtr );
для успешного выполнения, если basePtr указывает на производный класс с шаблоном функции, совместимым с вызовом, и для исключения в противном случае.
Мне кажется, что boost :: serialize делает нечто похожее, но я не могу понять, как (может быть, это повторное внедрение отношений наследования в C ++ с помощью шаблонов, я вижу функцию This (), возвращающую самый производный указатель, но это действительно сбивает с толку ...)
Заранее еще раз спасибо за помощь!