C++如何通过模板让构造函数按输入type参数初始化不同策略类对象
错误原因
你遇到的编译错误核心是C++模板的实例化是编译期行为,而switch分支是运行时分支:
当你调用不同参数的SellContext构造函数时,编译器会为每一组Ts参数实例化一整个构造函数的实现,不管运行时会走到哪个switch分支,所有分支的代码都必须是合法可编译的。
举个例子:当你调用无额外参数的SellContext(SellContext::type::NORMAL)时,编译器实例化出的构造函数里,case DISCOUNT分支的代码是new SellDiscount(),但SellDiscount没有无参构造函数,自然编译报错,其他报错也都是同理。
优雅解决方案
方案1:C++17及以上,编译期标签分发(推荐,兼容策略模式同类型要求)
不需要修改现有策略类,也不需要重载多个构造函数,还能保证所有SellContext是同一个类型,可以存入同一个容器:
#include <iostream> #include <memory> // 用智能指针避免内存泄漏 using namespace std; // 原有Strategy、SellNormal、SellDiscount、SellReturn类逻辑不变,仅补全虚析构 class Strategy { public: virtual double GetResult(double) = 0; virtual ~Strategy() = default; // 基类加虚析构,保证子类正确释放 }; class SellNormal: public Strategy { public: double GetResult(double original) override { return original; } }; class SellDiscount: public Strategy { double rate; public: SellDiscount(double r){ rate = r; } double GetResult(double original) override { return original * rate; } }; class SellReturn: public Strategy { int fulfill; int reduce; public: SellReturn(int f, int r): fulfill(f), reduce(r){} double GetResult(double original) override { return original - (int(original) / fulfill) * reduce; } }; class SellContext{ unique_ptr<Strategy> s; // 用智能指针自动管理内存 // 空标签类型,用于编译期重载区分 struct NormalTag {}; struct DiscountTag {}; struct ReturnTag {}; // 编译期重载的创建逻辑,不匹配的分支不会被实例化 template<typename... Ts> unique_ptr<Strategy> create(NormalTag, Ts&&... args) { return make_unique<SellNormal>(forward<Ts>(args)...); } template<typename... Ts> unique_ptr<Strategy> create(DiscountTag, Ts&&... args) { return make_unique<SellDiscount>(forward<Ts>(args)...); } template<typename... Ts> unique_ptr<Strategy> create(ReturnTag, Ts&&... args) { return make_unique<SellReturn>(forward<Ts>(args)...); } public: enum type{ NORMAL, DISCOUNT, RETURN }; template<typename ...Ts> SellContext(type t, Ts... args){ if constexpr (sizeof...(Ts) == 0) {} // 避免空参数包编译警告 switch(t){ case NORMAL: s = create(NormalTag{}, args...); break; case DISCOUNT: s = create(DiscountTag{}, args...); break; case RETURN: s = create(ReturnTag{}, args...); break; } } double getResult(double original){ return s->GetResult(original); } }; int main(){ SellContext c1(SellContext::type::NORMAL); SellContext c2(SellContext::type::DISCOUNT, 0.8); SellContext c3(SellContext::type::RETURN, 300, 100); cout << c1.getResult(1000) << '\n'; // 输出1000 cout << c2.getResult(1000) << '\n'; // 输出800 cout << c3.getResult(1000) << '\n'; // 输出700 }
方案2:C++11兼容方案
如果只能用C++11标准,可以把策略创建逻辑抽到静态重载函数,避免模板全分支编译的问题:
// 其他策略类代码和上文一致,仅修改SellContext部分 class SellContext{ Strategy* s; static Strategy* create(SellContext::type t) { if (t == NORMAL) return new SellNormal(); abort(); // 参数不匹配直接终止,可自行替换为自定义错误处理 } static Strategy* create(SellContext::type t, double rate) { if (t == DISCOUNT) return new SellDiscount(rate); abort(); } static Strategy* create(SellContext::type t, int fulfill, int reduce) { if (t == RETURN) return new SellReturn(fulfill, reduce); abort(); } public: enum type{ NORMAL, DISCOUNT, RETURN }; template<typename ...Ts> SellContext(type t, Ts... args): s(create(t, args...)) {} ~SellContext() { delete s; } double getResult(double original){ return s->GetResult(original); } };
内容的提问来源于stack exchange,提问作者Coding-pupupu
相关产品推荐
相关产品推荐

