想象一下下面的场景,我们有一个适用于许多不同场景的pod容器类型:
struct vec2 {
float x, y;
};
其中一些场景可能存储速度、位置、加速度等。现在假设我们希望能够独立处理所有这些场景,同时使类型系统工作,并维护vec2
的语义,即velocity.x
、acceleration.y
等
struct Simulation {
template<typename T>
static void handle() { fprintf( stdout, "Generic handlern" ); }
};
template<>
void Simulation::handle<vec2>() { fprintf( stdout, "generic vec2 handlern" ); }
template<>
void Simulation::handle<position>() { fprintf( stdout, "position handlern" ); }
template<>
void Simulation::handle<velocity>() { fprintf( stdout, "velocity handlern" ); }
template<>
void Simulation::handle<acceleration>() { fprintf( stdout, "acceleration handlern" ); }
在上述情况下,使用typedef
或using
将无法完成任务,因为根据标准,它们不会影响别名的typeid,因此不能执行以下操作:
using position = vec2;
下一个尝试可能是尝试从pod继承,即:
struct position : public vec2 {}
然而,这具有的缺点是new
和delete
现在被破坏,因为pod类型上没有虚拟析构函数,并且position
作为pod的资格也由于继承而丢失。
然后可以说,简单地复制和粘贴vec2
的代码并将其重命名为所需类型就可以满足所有要求,但这也有几个问题:
- 复制粘贴代码不正确*
- 在上面的例子中,vec2没有相关的运算符或类似的东西,但任何有用的vec2类都肯定会有这个,在这种情况下,在重命名数据结构时复制和粘贴将非常容易出错,尤其是在其中一个运算符需要修复的情况下
vec2也可以封装在新型中
struct position {
vec2 pos;
};
但是这个解决方案打破了维护语义的要求。
另一件需要注意的事情是,我们可能无法控制vec2的实现(即,在使用类似GLM的库的情况下(。
我觉得模板应该提供必要的解决方案,但无法确定它的工作方式。因此,问题是重新定义类型的正确方法是什么,同时保持类型的语义,但最终使用不同的类型id?
*-不是在所有情况下,但这一次肯定是
也许是这样的。
template <typename Tag>
struct vec2 {
float x, y;
};
using position = vec2<struct PositionTag>;
using velocity = vec2<struct VelocityTag>;
position
和velocity
将具有相似的行为,但是是不同的类型。
您可以将vec2
定义为模板类,并使用枚举标志来表示其存储类型。
enum class data_type {
position, velocity, acceleration
};
template<data_type>
struct vec2 {
float x, y;
};
vec2<data_type::position> p;
vec2<data_type::velocity> v;
vec2<data_type::acceleration> a;