您如何重新定义 POD 类型,使其在保持语义的同时被视为独特的类型?
How can you redefine a POD type such that it is considered a distinct type while maintaining the semantics?
想象以下场景,我们有一个适用于许多不同场景的 pod 容器类型:
struct vec2 {
float x, y;
};
其中一些场景可能会存储速度、位置、加速度等。现在假设我们希望能够独立处理所有这些场景,同时让类型系统正常工作并维护 vec2
,即velocity.x
,acceleration.y
,等等:
struct Simulation {
template<typename T>
static void handle() { fprintf( stdout, "Generic handler\n" ); }
};
template<>
void Simulation::handle<vec2>() { fprintf( stdout, "generic vec2 handler\n" ); }
template<>
void Simulation::handle<position>() { fprintf( stdout, "position handler\n" ); }
template<>
void Simulation::handle<velocity>() { fprintf( stdout, "velocity handler\n" ); }
template<>
void Simulation::handle<acceleration>() { fprintf( stdout, "acceleration handler\n" ); }
在上述情况下,使用 typedef
或 using
都不会完成这项工作,因为它们不会按照标准影响别名的 typeid,因此不能这样做:
using position = vec2;
下一次尝试可能是尝试从 pod 继承,即:
struct position : public vec2 {}
然而,这有一个缺点,即 new
和 delete
现在被破坏了,因为 pod 类型上没有虚拟析构函数,而且 position
是一个 pod 的资格因继承而失传
那么可以说,简单地复制和粘贴 vec2
的代码并将其重命名为所需的类型就可以满足所有要求,但是这也有几个问题:
- 复制粘贴代码错误*
- 在上面的例子中,vec2 没有关联的运算符或类似的东西,但是任何有用的 vec2 class 肯定会有这个,并且在这种情况下重命名数据结构时复制和粘贴会很容易出错特别是如果其中一个运算符需要修复
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
定义为模板 class 并使用枚举标志来表示其存储类型。
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;
想象以下场景,我们有一个适用于许多不同场景的 pod 容器类型:
struct vec2 {
float x, y;
};
其中一些场景可能会存储速度、位置、加速度等。现在假设我们希望能够独立处理所有这些场景,同时让类型系统正常工作并维护 vec2
,即velocity.x
,acceleration.y
,等等:
struct Simulation {
template<typename T>
static void handle() { fprintf( stdout, "Generic handler\n" ); }
};
template<>
void Simulation::handle<vec2>() { fprintf( stdout, "generic vec2 handler\n" ); }
template<>
void Simulation::handle<position>() { fprintf( stdout, "position handler\n" ); }
template<>
void Simulation::handle<velocity>() { fprintf( stdout, "velocity handler\n" ); }
template<>
void Simulation::handle<acceleration>() { fprintf( stdout, "acceleration handler\n" ); }
在上述情况下,使用 typedef
或 using
都不会完成这项工作,因为它们不会按照标准影响别名的 typeid,因此不能这样做:
using position = vec2;
下一次尝试可能是尝试从 pod 继承,即:
struct position : public vec2 {}
然而,这有一个缺点,即 new
和 delete
现在被破坏了,因为 pod 类型上没有虚拟析构函数,而且 position
是一个 pod 的资格因继承而失传
那么可以说,简单地复制和粘贴 vec2
的代码并将其重命名为所需的类型就可以满足所有要求,但是这也有几个问题:
- 复制粘贴代码错误*
- 在上面的例子中,vec2 没有关联的运算符或类似的东西,但是任何有用的 vec2 class 肯定会有这个,并且在这种情况下重命名数据结构时复制和粘贴会很容易出错特别是如果其中一个运算符需要修复
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
定义为模板 class 并使用枚举标志来表示其存储类型。
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;