如何在同一容器中存储模板类不同实例并调用其专属方法
问题描述
现有如下类结构:
class A { // 包含虚方法 }; template<typename T> class B : public A { public: void ANewMethodSpecificOfClassB(/* 参数 */) { /* 实现 */ }; };
需要将不同实例化的B类对象(如B<instanciation1> south_park、B<instanciation2> family_guy)放入同一个容器(例如std::vector),并调用它们的ANewMethodSpecificOfClassB方法。
遇到的困境:
- 无法直接声明
std::vector<B>,因为B是模板类,只有特定实例化后的B<T>才是具体类。 - 使用
std::vector<std::shared_ptr<A>>存储时,无法直接调用ANewMethodSpecificOfClassB——该方法未在基类A中定义,且无法修改A的代码。
可行解决方案
方案1:类型擦除自定义包装器
通过创建包装类封装B<T>的方法调用逻辑,实现类型擦除,让不同B<T>实例可以统一存储。
实现步骤:
- 定义抽象接口类,声明需要调用的方法:
class BWrapperInterface { public: virtual ~BWrapperInterface() = default; virtual void callANewMethod(/* 匹配ANewMethodSpecificOfClassB的参数 */) = 0; };
- 实现模板子类,持有具体
B<T>实例并实现接口方法:
template<typename T> class BWrapper : public BWrapperInterface { private: B<T>& instance; // 可替换为std::shared_ptr<B<T>>,根据对象生命周期需求调整 public: explicit BWrapper(B<T>& obj) : instance(obj) {} void callANewMethod(/* 参数 */) override { instance.ANewMethodSpecificOfClassB(/* 传递参数 */); } };
- 创建容器存储包装器智能指针并调用方法:
#include <vector> #include <memory> int main() { B<instanciation1> south_park; B<instanciation2> family_guy; std::vector<std::unique_ptr<BWrapperInterface>> container; container.emplace_back(std::make_unique<BWrapper<instanciation1>>(south_park)); container.emplace_back(std::make_unique<BWrapper<instanciation2>>(family_guy)); // 遍历调用每个实例的方法 for (auto& wrapper : container) { wrapper->callANewMethod(/* 传入参数 */); } return 0; }
方案2:使用std::variant(已知所有B<T>类型时)
如果所有需要存储的B<T>实例类型明确已知,可通过std::variant存储不同类型,再用std::visit统一调用方法。
示例代码:
#include <vector> #include <variant> // 定义包含所有目标类型的variant using BVariant = std::variant<B<instanciation1>, B<instanciation2>>; int main() { B<instanciation1> south_park; B<instanciation2> family_guy; std::vector<BVariant> container; container.push_back(south_park); container.push_back(family_guy); // 定义调用方法的逻辑 auto call_method = [](auto& b_instance) { b_instance.ANewMethodSpecificOfClassB(/* 参数 */); }; // 遍历容器执行调用 for (auto& var : container) { std::visit(call_method, var); } return 0; }
该方法局限性在于必须提前明确所有要存储的B<T>类型,无法动态添加未知类型。
方案3:dynamic_cast类型转换(不推荐,代码冗余)
若能确定每个元素的具体类型,可将A*动态转换为对应B<T>*后调用方法,但需逐个判断类型,代码冗余且扩展性差。
示例代码:
#include <vector> #include <memory> int main() { std::vector<std::shared_ptr<A>> container; container.push_back(std::make_shared<B<instanciation1>>()); container.push_back(std::make_shared<B<instanciation2>>()); for (auto& ptr : container) { if (auto b1_ptr = dynamic_cast<B<instanciation1>*>(ptr.get())) { b1_ptr->ANewMethodSpecificOfClassB(/* 参数 */); } else if (auto b2_ptr = dynamic_cast<B<instanciation2>*>(ptr.get())) { b2_ptr->ANewMethodSpecificOfClassB(/* 参数 */); } // 新增类型需添加更多else if分支 } return 0; }
仅适合类型极少且固定的场景,不推荐用于复杂或需扩展的场景。
内容的提问来源于stack exchange,提问作者roschach
相关产品推荐
相关产品推荐

