根据您的需要,我可以想到三种可能的解决方案。在我的示例中,我已将原始指针替换为 unique_ptrs。
案例 1:您不需要每个派生类型的基类型相同。
使用CRTP 允许基类型将自身作为派生类型调用。示例实现:
template <typename DerivedType>
class Base {
template <typename F>
auto invoke_as_derived(F&& f) {
return std::forward<F>(f)(static_cast<DerivedType*>(this));
}
};
class Derived : public Base<DerivedType> {};
用法:
std::unique_ptr<Base<Derived>> b = std::make_unique<Derived>();
b->invoke_as_derived(foo);
由于您提到使用基指针列表,这可能对您不起作用。
案例 2:您需要一个共享的基类型,但在您的类型层次结构中只有一层,并且没有虚拟方法。
使用std::variant 和std::visit。
class Derived {};
using Base = std::variant<Derived, /* other derived types */>;
auto foo(Derived*) { ... }
class FooCaller {
operator ()(Derived& d) {
return foo(&d);
}
// Overload for each derived type.
}
用法:
Base b = Derived();
std::visit(FooCaller{}, b);
案例 3:您需要一个基本类型,但还需要虚拟方法和/或类型层次结构中的其他层。
您可以试试visitor pattern。它需要一些样板文件,但根据您的需要,它可能是最佳解决方案。实现草图:
class Visitor; // Forward declare visitor.
class Base
{
public:
virtual void accept(Visitor& v) = 0;
};
class Derived : public Base
{
public:
void accept(Visitor& v) final { v.visit(*this); }
};
struct Visitor
{
virtual void visit(Derived&) = 0;
// One visit method per derived type...
};
struct FooCaller : public Visitor
{
// Store return value of call to foo in a class member.
decltype(foo(new Derived())) return_value;
virtual void visit(Derived& d)
{
return_value = foo(&d);
}
// Override other methods...
};
用法:
std::unique_ptr<Base> b = std::make_unique<Derived>();
FooCaller foo_caller;
b->accept(foo_caller);
您可以编写一个访问者,它将一个函数应用于元素,这样您就不必为所有函数重复此操作。或者,如果您可以更改函数本身,则可以将您的函数替换为访问者类型。
编辑:将调用语法简化为foo(b)
为每个要向其传递Base 对象的函数重载集定义一个重载。示例,使用第三种技术:
auto foo(Base* b) {
FooCaller foo_caller;
b->accept(foo_caller);
return std::move(foo_caller.return_value);
}
现在foo(b.get()) 将在运行时委托给foo 的适当重载。