使用可变参数模板判断基类指针是否指向指定派生类实例
解决基类指针类型判断与派生类类型约束问题
一、用可变参数模板判断基类指针是否属于指定派生类集合
咱们可以借助dynamic_cast的特性——如果基类指针实际指向的是目标派生类实例,dynamic_cast会返回非空指针,否则返回nullptr。结合可变参数模板,写一个递归的判断函数就能搞定:
#include <type_traits> // 终止条件:没有更多类型需要检查 template<typename Target> bool is_one_of(const Base* ptr) { return dynamic_cast<const Target*>(ptr) != nullptr; } // 可变参数递归版本:先检查当前类型,再递归检查剩余类型 template<typename First, typename... Rest> bool is_one_of(const Base* ptr) { return dynamic_cast<const First*>(ptr) != nullptr || is_one_of<Rest...>(ptr); }
使用起来很直观,比如你有DerivedA、DerivedB、DerivedC三个派生类,想判断Base* p是不是指向这三个中的一个:
if (is_one_of<DerivedA, DerivedB, DerivedC>(p)) { // p确实指向指定的派生类实例 }
注意:因为用到了
dynamic_cast,你的基类Base必须包含至少一个虚函数(你已经声明了虚析构函数,完全满足要求),否则dynamic_cast无法正常工作。
二、约束派生类中_stub的类型为指定集合之一
接下来处理带_stub的模板派生类。我们要在编译期就确保_stub的类型要么是当前模板参数T,要么是可变参数Supp...中的某一个。这里用C++17引入的std::disjunction结合std::is_same,配合static_assert就能给出清晰的编译错误提示:
template<typename T, typename... SupportedTypes> class DerivedWithStub : public Base { public: // 允许_stub的类型是T或SupportedTypes中的任意一种 template<typename StubT> DerivedWithStub(T value, DerivedWithStub<StubT, SupportedTypes...>* stub = nullptr) : _value(std::move(value)), _stub(stub) { // 编译期检查StubT是否在允许的类型集合里 static_assert( std::is_same_v<StubT, T> || std::disjunction_v<std::is_same<StubT, SupportedTypes>...>, "错误:Stub类型必须是T或者指定的支持类型之一!" ); } private: T _value; Base* _stub; // 用Base*符合多态场景,也可以根据需求保留具体类型信息 };
如果有人尝试用不允许的类型初始化_stub,比如:
DerivedWithStub<int, float> d1(42); DerivedWithStub<double, float> d2(3.14); d1 = DerivedWithStub<int, float>(100, &d2); // StubT是double,不在允许的集合(int、float)里
编译器会直接触发static_assert的错误提示,把问题扼杀在编译阶段,避免运行时踩坑。
如果你想更直接地在类模板层面约束,也可以把StubT作为类的模板参数,然后在类定义处添加检查:
template<typename T, typename StubT, typename... SupportedTypes> class DerivedWithStub : public Base { // 类定义时就做编译期检查 static_assert( std::is_same_v<StubT, T> || std::disjunction_v<std::is_same<StubT, SupportedTypes>...>, "错误:Stub类型不被允许!" ); // ... 成员变量和函数定义 };
这种方式更直接,但灵活性稍弱,因为StubT必须在声明类时就明确指定。
内容的提问来源于stack exchange,提问作者Lex
相关产品推荐
相关产品推荐

