如何创建依赖其他模板的Instance类模板,消除精度类型冗余?
我有一组均继承自基类RigidBody的类(注:以下代码中,Tp用于指定精度类型(如float),Tc用于指定颜色类型(如RGB)):
template <typename Tp> class RigidBody { ... };
这些类包括Model、Assembly、Light等,以下是Model的示例:
template <typename Tp, typename Tc> class Model : public RigidBody<Tp> { ... };
我现在需要编写一个模板化的Instance类,作为上述所有类的包装器,同时也继承自RigidBody。我当前的实现如下:
template <typename Tp, typename ObjectType> class Instance : public RigidBody<Tp> { ... };
虽然该实现可用,但像这样特化时感觉繁琐:
Instance<float, Model<float,RGB>>
因为float是重复的。我更希望能直接使用Instance<Model<float,RGB>>。
我了解过“模板模板”语法,但似乎无法解决我的问题。我尝试过:
template <template<typename Tp,typename Tc> typename ObjectType> class Instance : public RigidBody<Tp> { ... };
但这无法运行,因为在类声明的主体中Tp是未声明的标识符。
是否有方法实现我想要的行为?我绝不允许Instance的精度与其包装的对象不同。
方法一:类型萃取(通用兼容C++11及以上)
核心思路是通过类型萃取模板从包装的对象类型中提取出它的精度类型Tp,让Instance只需要一个模板参数即可。
步骤1:定义类型萃取模板
为每个继承自RigidBody的类编写特化的萃取模板,用来暴露它的精度类型:
// 基础萃取模板,默认情况(无需实现,仅用于特化) template <typename T> struct RigidBodyTraits; // 针对Model的特化 template <typename Tp, typename Tc> struct RigidBodyTraits<Model<Tp, Tc>> { using PrecisionType = Tp; }; // 针对Assembly的特化(示例) template <typename Tp> struct RigidBodyTraits<Assembly<Tp>> { using PrecisionType = Tp; }; // 针对Light的特化(示例) template <typename Tp> struct RigidBodyTraits<Light<Tp>> { using PrecisionType = Tp; };
步骤2:重构Instance类
修改Instance,让它仅接受ObjectType作为模板参数,通过萃取得到Tp并继承RigidBody<Tp>:
template <typename ObjectType> class Instance : public RigidBody<typename RigidBodyTraits<ObjectType>::PrecisionType> { private: ObjectType wrapped_obj; // 包装的对象 // 其他成员函数与逻辑... };
使用方式
现在你可以直接写出简洁的声明:
Instance<Model<float, RGB>> my_instance;
这种方式天然保证Instance的精度和包装对象完全一致——因为Tp是从对象类型中直接提取的,不存在手动传入错误的可能。
方法二:模板推导指南(C++17及以上)
如果你的项目使用C++17或更高版本,可以保留原有的双参数Instance模板,通过推导指南让编译器自动推导Tp,避免重复书写。
步骤1:保留双参数Instance模板
template <typename Tp, typename ObjectType> class Instance : public RigidBody<Tp> { private: ObjectType wrapped_obj; // 其他成员... };
步骤2:添加推导指南
为每个继承自RigidBody的类编写推导规则:
// 针对Model的推导指南 template <typename Tp, typename Tc> Instance(Model<Tp, Tc>) -> Instance<Tp, Model<Tp, Tc>>; // 针对Assembly的推导指南(示例) template <typename Tp> Instance(Assembly<Tp>) -> Instance<Tp, Assembly<Tp>>;
使用方式
通过构造函数创建对象时,编译器会自动推导模板参数:
Model<float, RGB> my_model; Instance my_instance(my_model); // 自动推导为Instance<float, Model<float, RGB>>
注意:这种方式仅在通过构造函数推导时有效,如果需要直接用模板参数声明(如Instance<Model<float,RGB>>),还是方法一更合适。
额外保障:强制精度一致性
如果你担心有人错误使用双参数模板传入不一致的Tp,可以添加静态断言强制校验:
template <typename Tp, typename ObjectType> class Instance : public RigidBody<Tp> { private: ObjectType wrapped_obj; // 静态断言:确保Tp与包装对象的精度一致 static_assert(std::is_same_v<Tp, typename RigidBodyTraits<ObjectType>::PrecisionType>, "Instance的精度类型必须与包装对象的精度类型完全一致"); // 其他成员... };
这样一旦出现Instance<double, Model<float, RGB>>这类错误写法,编译器会直接抛出清晰的错误提示。
内容的提问来源于stack exchange,提问作者Chris Gnam

