由于我之前的问题表述有些混乱,所以现在我想清楚地说明我的目标。
我拥有以下内容(暂时忽略继承关系,重点关注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" );
}
};
现在我可以引入任意新类,这些类根据重载数据函数执行不同的操作,我们将在接下来的内容中将其称为“访问器”:
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
目标是让这个最后的调用成功。我考虑过的方法(以及为什么不可行):
第一种选择:让所有的访问器都继承一个共同的基类,比如AccessorBase,然后在Base中编写。
virtual state( AccessorBase& a ) const = 0;
并在X中实现所需的代码(调用状态的语法略有不同,但这可以修复)。 问题在于,AccessorBase将需要为数据函数中第二个参数作为可能类型的每个虚拟函数。由于这些类型可以是用户定义的类(请参见类组合的情况,X具有Y作为数据成员),我不知道如何使此策略能够工作。
第二个选择:为每个Accessor在Base中创建一个虚拟函数。这违反了开/闭原则,因为添加新的Accessor类(比如TxtArchive)需要修改基类。
我理解为什么虚拟成员函数不能被模板化(不同编译单元中可能有不同的vtbls)。但是,对我来说似乎应该有一种解决办法...... Base知道它实际上是X类型,并且Accessor的类型始终是显式的,因此问题只是找到一种调用方式(对于类型为XmlArchive的Accessor):
state( xmlArchive, x ); //xmlArchive of type XmlArchive, x of type X
这将产生结果。
总结一下,我想要的呼叫是:
state( myParameters, *basePtr );
如果basePtr指向一个与调用兼容的函数模板的派生类,则成功,否则抛出异常。
似乎boost::serialize做了类似的事情,但我无法弄清楚它是如何实现的(可能是通过模板在C++中重新实现继承关系,我看到一个This()函数返回最终派生指针,但这真的很令人困惑...)
再次感谢您的帮助!