如何在C++中无需指定Scheme_handle的工厂返回类型模板参数
问题:实现无需指定模板参数的Scheme_handle(无动态内存)
需求背景:
- 使用者通过Factory构造Scheme的特殊派生类,工厂方法必须返回Table的特化类
- 每个工厂方法对应两部分:返回的Table特化实例 + 与之兼容的Scheme特化类
- 核心诉求:
- 便捷性:使用者无需知晓Table与Scheme的对应关系,甚至不用知道工厂返回的Table类型
- 支持高级用户:可强制指定要使用的Scheme特化类(因此工厂仅返回Table,且无参数)
- 限制:不能使用动态内存,当前的Scheme_handle模板类需要用户指定模板参数,违背便捷性初衷
尝试代码:
#include<iostream> //#include<memory> // infeasible since dynamic allocation struct Table{ double data; }; struct Scheme{}; // struct Table_A: Table{}; struct Table_B: Table{}; // there are too many different scheme types for use of std::conditional_t // struct Scheme_A: Scheme{ Scheme_A(Table_A const& t){} }; struct Scheme_B: Scheme{ Scheme_B(Table_A const& t){} Scheme_B(Table_B const& t){} }; struct Factory{ /* factory implementation ... */ static Table_A make_x(){ return Table_A{}; } // it is mandatory that Factory returns Tables static Table_B make_y(){ return Table_B{}; } // otherwise, Table would not have been static Table_A make_z(){ return Table_A{}; } // included in this minimum code example }; // specializations still need a declaration in 2023 template<typename TableType> class Scheme_handle; // the following two lines of terrible boilerplate are necessary // because c++ wont allow template<> using Scheme_handle<Table_A> = Scheme_A; // same goes for typedef template<> class Scheme_handle<Table_A>: public Scheme_A{ public: Scheme_handle(Table_A const& t):Scheme_A(t){} }; // Table_A -> Scheme_A template<> class Scheme_handle<Table_B>: public Scheme_B{ public: Scheme_handle(Table_B const& t):Scheme_B(t){} }; // Table_B -> Scheme_B // here is the consumer code that I need to be handable struct MyClass{ // // what more needs the compiler so one can drop the "<Table_A>" ? Scheme_handle<Table_A> x1 = Factory::make_x(); // at least this gives me a handle to a scheme // Scheme_handle<Table_B> y1 = Factory::make_y(); // the consumer does not possibly want to remember the scheme type of y. Scheme_handle<Table_B> y2 = Factory::make_y(); // schemes arent singletons! // Scheme_B x2 = Factory::make_x(); // a table does not imply a scheme! // (but for each table there is a default compliant scheme, as defined by the mapping Scheme_handle : TableType -> SchemeType ) }; int main(){ MyClass q; }
解决方案
我们可以通过**C++推导指南(Deduction Guides)**让编译器自动推导Scheme_handle的模板参数,同时保留Table到Scheme的映射关系,完全满足无动态内存、便捷性与高级用户需求。
1. 重构Scheme_handle模板结构
先定义通用模板框架,再通过特化绑定Table与对应Scheme的类型关系:
// 通用模板声明 template<typename TableType> class Scheme_handle; // Table_A对应的特化:绑定Scheme_A template<> class Scheme_handle<Table_A> { public: using SchemeType = Scheme_A; Scheme_handle(const Table_A& table) : scheme(table) {} // 提供Scheme实例的访问接口 Scheme_A& get() { return scheme; } const Scheme_A& get() const { return scheme; } private: Scheme_A scheme; // 栈上存储,无动态内存分配 }; // Table_B对应的特化:绑定Scheme_B template<> class Scheme_handle<Table_B> { public: using SchemeType = Scheme_B; Scheme_handle(const Table_B& table) : scheme(table) {} Scheme_B& get() { return scheme; } const Scheme_B& get() const { return scheme; } private: Scheme_B scheme; };
2. 添加推导指南实现自动推导
通过推导指南,让编译器从传入的Table实例自动识别Scheme_handle的模板参数:
// 推导指南:根据Table类型自动推导Scheme_handle的模板参数 template<typename TableType> Scheme_handle(const TableType&) -> Scheme_handle<TableType>;
3. 简化使用者代码
现在使用者无需手动指定模板参数,编译器会自动完成推导,同时高级用户仍可直接指定Scheme特化类:
struct MyClass{ // 自动推导为Scheme_handle<Table_A>,无需写<Table_A> Scheme_handle x1 = Factory::make_x(); // 自动推导为Scheme_handle<Table_B> Scheme_handle y1 = Factory::make_y(); Scheme_handle y2 = Factory::make_y(); // 高级用户仍可直接使用指定的Scheme特化类 Scheme_B x2 = Factory::make_x(); };
完整可运行代码
#include<iostream> struct Table{ double data; }; struct Scheme{}; struct Table_A: Table{}; struct Table_B: Table{}; struct Scheme_A: Scheme{ Scheme_A(Table_A const& t){} }; struct Scheme_B: Scheme{ Scheme_B(Table_A const& t){} Scheme_B(Table_B const& t){} }; struct Factory{ static Table_A make_x(){ return Table_A{}; } static Table_B make_y(){ return Table_B{}; } static Table_A make_z(){ return Table_A{}; } }; // 通用Scheme_handle模板声明 template<typename TableType> class Scheme_handle; // Table_A对应的特化 template<> class Scheme_handle<Table_A> { public: using SchemeType = Scheme_A; Scheme_handle(const Table_A& table) : scheme(table) {} Scheme_A& get() { return scheme; } const Scheme_A& get() const { return scheme; } private: Scheme_A scheme; }; // Table_B对应的特化 template<> class Scheme_handle<Table_B> { public: using SchemeType = Scheme_B; Scheme_handle(const Table_B& table) : scheme(table) {} Scheme_B& get() { return scheme; } const Scheme_B& get() const { return scheme; } private: Scheme_B scheme; }; // 推导指南:实现自动模板参数推导 template<typename TableType> Scheme_handle(const TableType&) -> Scheme_handle<TableType>; struct MyClass{ // 自动推导模板参数,无需手动指定 Scheme_handle x1 = Factory::make_x(); Scheme_handle y1 = Factory::make_y(); Scheme_handle y2 = Factory::make_y(); // 高级用户可直接指定Scheme特化类 Scheme_B x2 = Factory::make_x(); }; int main(){ MyClass q; }
方案优势
- 零手动模板参数:普通用户无需记忆Table与Scheme的对应关系,编译器自动完成推导
- 无动态内存:所有Scheme实例都存储在Scheme_handle内部,完全基于栈分配
- 保留灵活性:高级用户仍可直接实例化指定的Scheme特化类,不限制自定义场景
内容的提问来源于stack exchange,提问作者HKoplin
相关产品推荐
相关产品推荐

