调用派生类的模板函数
calling a template function of a derived class
我在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) { ... };
};
用法:
int main()
{
Base* BasePtr = new Derived1();
// The expected function to be called is Derived1::func<int>()
BasePtr->func<int>();
return 0; // :)
}
我无法使func成为虚拟,因为该语言不支持虚拟模板功能。
仅当只有类具有模板参数时才允许使用,而当类中的函数具有模板参数时,才允许这样做。
我已经看到在Boost.Serialization中解决了类似的问题,但无法理解解决方案。
谢谢
科比·梅厄
实现奇怪的重复模板模式 (CTRP)。
插图:
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);
现有的两种解决方案将动态多态性换成了静态多态性。如果没有关于手头问题的更多细节,就不可能知道这是否是一种有效的方法,因为它基本上打破了多态层次结构:使用 CRTP 时,没有单个基类,而是它们的一系列。您不能将Derived1
和Derived2
的对象保存在同一个容器中,因为它们是不相关的......如果您只需要共享代码,那么这是一个很好的解决方案,但如果您需要动态多态性,则不然。查看访客模式和类似问题的双重调度。
如果需要动态多态性,可以尝试实现双重调度(这很痛苦,但如果层次结构足够小,则可行。基本上创建两个不同的层次结构,一个根植于Base
,另一个充当手动调度程序。根植于 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); }
};
我使用的名称在访问者模式中很常见,这种方法与该模式非常相似(我不敢称它为访问者模式,但方法相似,所以我只是借用了命名约定)。
用户代码类似于:
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
}
预先声明i
和c
的要求可以通过(如果可能的话)将函数的参数从引用更改为 const 引用来释放。实际的魔术在于,在[1]中,C++单一调度机制门槛调度对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++中非常不寻常,如果您解释手头的实际问题,可能会有更好的更简单的解决方案,您所说的是原始问题的解决方案的要求:动态调度到模板。
看看这个,它将帮助您实现 CRTP。
- 为什么使用 "this" 指针调用派生成员函数?
- 在派生函数中指定void*参数
- 如何通过派生类函数更改基类中的向量
- 如何委托派生类使用其父构造函数?
- 如何使用单独文件中的派生类访问友元函数对象
- 使用基类指针创建对象时,缺少派生类析构函数
- 在 C++ 中用派生类型重写成员函数
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 如果基类包含双指针成员,则派生类的构造函数
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 在派生类中绑定非静态模板化成员函数
- 使用 std::variant<...时调用 BaseState 函数而不是派生函数>
- C++:为什么无法在派生类中访问受保护的构造函数?
- C++派生的类构造函数
- C++重载函数,一个采用基类的参数,另一个采用派生类的参数
- C ++如何在原始抽象类中创建一个函数,该函数接受派生类的输入
- 覆盖虚拟函数 - 派生类具有不同的参数
- 正在调用基方法,而不是从构造函数派生方法
- 从具有相同虚拟函数的父函数派生时如何调用子函数
- 从派生类对象调用基类函数.派生类构造函数中设置的基类数据成员