kob*_*eir 5 c++ templates derived boost-serialization
我在C++中遇到问题,在调用派生类的函数的同时有一个指向基类的指针.
编辑:一些答案提到我CRTP
但我的观点是我需要一个指向"Base*"类而不是"Base*"的指针,因为我不知道当前正在处理的类型(当前实例是从某种工厂创建的).
class Base
{
..
template<typename T>
func (T arg) { ... };
};
class Derived1 : public Base
{
...
template<typename T>
func (T arg) { ... };
};
class Derived1 : public Base
{
...
template<typename T>
func (T arg) { ... };
};
Run Code Online (Sandbox Code Playgroud)
int main()
{
Base* BasePtr = new Derived1();
// The expected function to be called is Derived1::func<int>()
BasePtr->func<int>();
return 0; // :)
}
Run Code Online (Sandbox Code Playgroud)
我不能使func虚拟,因为该语言不支持虚拟模板功能.
仅当类只有模板参数时才允许,但如果其中的函数具有模板参数则不允许.
我已经看到在Boost.Serialization中解决了类似的问题,但无法理解解决方案.
谢谢,
Koby Meir
插图:
template<typename D>
class Base
{
public:
template<typename T>
void func (T arg)
{
static_cast<D*>(this)->func(arg);
}
};
class Derived1 : public Base<Derived1>
{
public:
template<typename T>
void func (T arg) { /*...*/ }
};
Base<Derived1> *basePtr = new Base<Derived1>();
basePtr->func(100);
Run Code Online (Sandbox Code Playgroud)
两种现有的解决方案交换静态多态的动态多态性.没有手头上的问题的详细信息,这是不可能知道这是否是一个有效的方法还是不行,因为它基本上打破了多态的层次结构:与CRTP没有单一的基类,而是一个家庭他们.你不能让的对象Derived1和Derived2他们无关......这是一个很好的解决方案,如果你需要的是共享代码,但如果你需要动态多态性在同一容器中.看看访客模式和双重发送类似问题.
如果你需要动态多态,你可以尝试实现双重调度(这是一种痛苦,但如果层次结构足够小,则是可行的.基本上创建两个不同的层次结构,一个以root为根,Base另一个用作手动调度程序的一部分. root的Base将有一个虚方法apply,第二个层次结构将为第一个层次结构中的每个类型提供虚函数:
class Base;
class Derived1; // inherits from Base, implements Visitor
class Derived2; // inherits from either Base or Derived2
struct Visitor {
virtual void visit( Base& ) = 0; // manually unrolled for all types
virtual void visit( Derived1& ) = 0;
virtual void visit( Derived2& ) = 0;
};
struct Base {
virtual void apply( Visitor& v ) { // manually replicate this in Derived1, 2
v.visit( *this );
}
template <typename T> void foo(T); // implement
};
template <typename T>
struct FooCaller : Visitor {
T& ref_value;
FooCaller( T& v ) : ref_value(v) {}
template <typename U> void call_foo( U& o ) {
o.foo(ref_value);
}
virtual void visit( Base & b ) { call_foo(b); }
virtual void visit( Derived1 & d1 ) { call_foo(d1); }
virtual void visit( Derived2 & d2 ) { call_foo(d2); }
};
Run Code Online (Sandbox Code Playgroud)
我使用的名称在访问者模式中很常见,这种方法与该模式非常相似(我不敢称之为访问者模式,但方法类似,所以我只是借用了命名约定).
用户代码类似于:
int main() // main returns int, not void!!!
{
Base* BasePtr = new Derived1();
int i = 5;
FooCaller<int> c(i)
BasePtr->apply(c); // [1] magic happens here
}
Run Code Online (Sandbox Code Playgroud)
通过从const-references的引用更改(如果可能)函数的参数,可以释放声明i和chand之前的要求.实际的魔力是在[1]中C++单一调度机制sill调度调用Derived1::apply,因为这是指向的对象的动态类型BasePtr.在那一点上,它将Visitor::visit( Derived1& )自己称为参数.这将再次通过单个调度机制调度到FooCaller<int>::visit( Derived1& ),并且此时两个对象都已解析为其静态类型.当FooCaller<int>::visit调用call_foo参数U被推断为Derived1,当它调用Derived1::foo参数被推断为int并且它最终调用Derived1::foo<int>...虽然有几个循环和间接...
根据您的特定用例,这可能过于复杂(如果静态多态性如CRTP可行)或太难维护(如果层次结构很大:对于层次结构中的每个新元素,Base您将必须更新层次结构中的所有类型的Visitor),所以如果你能避免这种情况的复杂性,完美.但在某些情况下,你需要这个.
另外请注意,这是最复杂的完全动态的解决方案,在两者之间有其他的选择,这取决于它是什么,你需要运行时多态性......它可能的情况是,您的层次结构模型短裤的访问者,而您只需要手动展开将在内部调度到模板的不同虚拟函数,在这种情况下,上述复杂性的一半将消失.
另请注意,这在C++中是非常不寻常的,如果您解释手头的实际问题,可能会有更好的更简单的解决方案,您所说的是解决原始问题的要求:动态调度到模板.
| 归档时间: |
|
| 查看次数: |
4884 次 |
| 最近记录: |