C++中如何实现由运行时布尔标志决定类型的全局对象(无C++17)
问题说明
需要根据运行时的布尔值flag选择构造type1_t或type2_t类型的对象,后续对该对象执行完全一致的操作。直接在if/else分支内定义对象会受作用域限制无法在分支外访问,且无法提前声明对象(类型运行时才确定),同时编译环境不支持C++17,无法使用std::variant。
原始待实现逻辑代码:
if (flag) { type1_t object = ..... } else { type2_t object = ..... } // do the same thing with object
其中type1_t、type2_t均为自定义类。
可行实现方案
以下方案全兼容C++11及以上标准,根据实际场景选择即可:
方案1:模板函数封装公共逻辑(最省事,零侵入)
不需要修改原有类定义,也不需要加额外的继承关系,只要两个类型对后续操作的调用接口签名一致就可以用。把要对对象执行的所有相同逻辑抽到模板函数里,在每个分支内构造完对象直接调用即可。// 封装所有要对object执行的相同操作 template <typename T> void process_object(T& object) { // 直接写逻辑即可,比如object.run(), object.get_value()这类调用 // 只要type1_t和type2_t都支持这里用到的接口就能正常编译 } // 业务逻辑 if (flag) { type1_t object = /* 对应构造逻辑 */; process_object(object); } else { type2_t object = /* 对应构造逻辑 */; process_object(object); }这个方案没有任何额外开销,适合不需要把对象传出if/else作用域、对象生命周期仅覆盖操作逻辑的场景。
方案2:公共基类+运行时多态(适合对象生命周期长、需要跨作用域传递的场景)
把两个类型要执行的公共操作抽成抽象基类的虚函数,用智能指针持有基类指针,在分支内构造对应派生类对象即可,后续通过基类指针调用公共方法。// 定义公共接口 class object_base { public: virtual ~object_base() = default; // 声明所有要对对象执行的操作为纯虚函数 virtual void do_action() = 0; }; // 让原有类型继承基类,实现对应接口 class type1_t : public object_base { // 原有逻辑保留 public: void do_action() override { // type1_t对应的操作实现 } }; class type2_t : public object_base { // 原有逻辑保留 public: void do_action() override { // type2_t对应的操作实现 } }; // 业务逻辑 std::unique_ptr<object_base> obj; if (flag) { obj.reset(new type1_t(/*构造参数*/)); // C++11没有make_unique直接用reset+new即可 } else { obj.reset(new type2_t(/*构造参数*/)); } // 分支外直接调用接口,也可以把obj传到其他函数/存到容器里 obj->do_action();如果不方便直接修改原有类,可以单独写两个适配类继承基类,内部持有原类型的实例,把调用转发过去就行,不侵入原有代码。
方案3:轻量双类型tagged union(无虚函数开销,适合固定两种类型的性能敏感场景)
不想用多态的话,可以自己实现一个只存两种类型的可辨识联合,手动管理类型标记和对象生命周期,不需要C++17支持:template <typename T1, typename T2> class Either { private: bool is_first_type; union { T1 t1_val; T2 t2_val; }; public: Either(const T1& v) : is_first_type(true), t1_val(v) {} Either(T1&& v) : is_first_type(true), t1_val(std::move(v)) {} Either(const T2& v) : is_first_type(false), t2_val(v) {} Either(T2&& v) : is_first_type(false), t2_val(std::move(v)) {} ~Either() { if (is_first_type) { t1_val.~T1(); } else { t2_val.~T2(); } } // 访问内部对象 template <typename Func> void visit(Func&& f) { if (is_first_type) { f(t1_val); } else { f(t2_val); } } // 拷贝、移动逻辑可以按需补全,简单场景下上面的代码足够用 };使用示例:
Either<type1_t, type2_t> obj = flag ? Either<type1_t, type2_t>(type1_t(/*构造参数*/)) : Either<type1_t, type2_t>(type2_t(/*构造参数*/)); obj.visit(process_object<type1_t>); // 直接传之前写的模板函数实例即可,兼容纯C++11
内容的提问来源于stack exchange,提问作者roulette01
相关产品推荐
相关产品推荐

