我一直在尝试一种声明强类型typedef的方法,以在编译阶段捕获某些类的错误。通常,我会将int类型定义为几种类型的id,或者将矢量类型化为位置或速度:
typedef int EntityID;
typedef int ModelID;
typedef Vector3 Position;
typedef Vector3 Velocity;
这可以使代码的意图更加清晰,但是经过一整夜的编码之后,人们可能会犯一些愚蠢的错误,例如比较不同类型的id或在速度上增加位置。
EntityID eID;
ModelID mID;
if ( eID == mID ) // <- Compiler sees nothing wrong
{ /*bug*/ }
Position p;
Velocity v;
Position newP = p + v; // bug, meant p + v*s but compiler sees nothing wrong
不幸的是,我发现有关强类型typedef的建议包括使用boost,至少对我而言这是不可能的(至少我确实有c ++ 11)。因此,经过一番思考,我想到了这个想法,并希望由某人来执行。
首先,您将基本类型声明为模板。该模板参数未用于定义中的任何内容,但是:
template < typename T >
class IDType
{
unsigned int m_id;
public:
IDType( unsigned int const& i_id ): m_id {i_id} {};
friend bool operator==<T>( IDType<T> const& i_lhs, IDType<T> const& i_rhs );
};
实际上,需要在类定义之前对Friend函数进行前向声明,这需要对模板类进行前向声明。
然后,我们记住基本类型的所有成员,只记得它是模板类。
最后,当我们要使用它时,我们将其typedef定义为:
class EntityT;
typedef IDType<EntityT> EntityID;
class ModelT;
typedef IDType<ModelT> ModelID;
现在,这些类型是完全分开的。例如,如果尝试改为向其提供ModelID,则采用EntityID的函数将引发编译器错误。除了必须将基本类型声明为模板之外,还伴随着一些问题,它也相当紧凑。
我希望有人对这个想法有意见或批评吗?
例如,在位置和速度的情况下,编写此书时想到的一个问题是,我无法像以前那样自由地在类型之间进行转换。在将向量乘以标量之前可以得到另一个向量的地方,所以我可以这样做:
typedef float Time;
typedef Vector3 Position;
typedef Vector3 Velocity;
Time t = 1.0f;
Position p = { 0.0f };
Velocity v = { 1.0f, 0.0f, 0.0f };
Position newP = p + v*t;
使用我的强类型typedef时,我必须告诉编译器,将多个Velocity乘以一个时间会产生一个Position。
class TimeT;
typedef Float<TimeT> Time;
class PositionT;
typedef Vector3<PositionT> Position;
class VelocityT;
typedef Vector3<VelocityT> Velocity;
Time t = 1.0f;
Position p = { 0.0f };
Velocity v = { 1.0f, 0.0f, 0.0f };
Position newP = p + v*t; // Compiler error
为了解决这个问题,我认为我必须明确地专门化每个转换,这可能会很麻烦。另一方面,此限制可以帮助防止其他类型的错误(例如,将“速度”乘以“距离”,这在该域中是没有意义的)。所以我很伤心,想知道人们是否对我的原始问题或解决问题的方法有任何看法。