这里主要讲述编译期的强制,C++不直接支持约束,本节描述了常用的约束设计方法。
1.1 must_have_base约束
此约束保证两个类之间的继承关系,代码如下:
template<
typename D,
typename B>
struct must_have_base
{
~must_have_base()
{
void (*p) (D*, B*) = constraints;
}
private:
static void constraints(D* pd, B* pb)
{
pb = pd;
}
};
pb是基类指针,pd是子类指针,因此pb=pd在D不是B的基类时会编译错误,从而保证了编译时检查D是否是B的基类。
但是有特殊的情况,1)D和B是同一个类。2)B是void类型。如此,做如下特化:
template <typename T>
struct must_have_base<T,T>
{
~must_have_base()
{
const int not_the_same_type = 0;
int i[not_the_same_type];
}
};
template <typename T>
struct must_have_base<T,void>
{
~must_have_base()
{
const int not_the_void = 0;
int i[not_the_void];
}
};
测试代码如下:
class A{};
class B : public A{};
class C : private A{};
class D;
int main(void)
{
must_have_base<B, A> oBA;
must_have_base<C, A> oCA; //error
must_have_base<D, A> oDA; //error
must_have_base<A, void> AVoid; //error
must_have_base<A, A> oAA; //error
return 0;
}
仅判断public继承关系,对于private继承不予判断。
1.2 must_be_subscriptable约束
此约束用来检查是否能进行下标引用操作,即[ ]操作。代码如下:
template< typename T >
struct must_be_subscriptable
{
~must_be_subscriptable()
{
void (*p) (T const &) = constraints;
}
private:
static void constraints(T const &T_is_not_subscriptable)
{
sizeof(T_is_not_subscriptable[0]);
}
};
这里使用了T_is_not_subscriptable[0]来使用[ ]操作符,编译器会进行判断,T是否重载了此操作符。
测试代码如下:
class subs
{
public:
int operator [](size_t index) const;
};
class not_subs{ };
int main(void)
{
must_be_subscriptable<int[1]> oInt;
must_be_subscriptable<int*> oIntPointer;
must_be_subscriptable<subs> oSubs;
must_be_subscriptable<not_subs> oNotSubs; //error
return 0;
}
1.3 must_be_subscriptable_as_decayable_pointer约束
此约束判断类型是否是原生指针,即形如T *p,p即是原生指针。template< typename T >
struct must_be_subscriptable_as_decayable_pointer
{
~must_be_subscriptable_as_decayable_pointer()
{
void (*p) (T const &) = constraints;
}
private:
static void constraints(T const &T_is_not_decay_subscriptable)
{
sizeof(0[T_is_not_decay_subscriptable]);
}
};
这里利用了原生指针的一个特性,即可作为下标使用,即如果定义了T *p,那么0[p]此表达式是合法的,代表着offset[pointer],与pointer[offset]等价,即p[0]。其他类型五次性质。
测试代码如下:
must_be_subscriptable_as_decayable_pointer<subs*> oPSubs;
must_be_subscriptable_as_decayable_pointer<const subs&> oRSubs; //error
must_be_subscriptable_as_decayable_pointer<subs> oNPSubs; //error
1.4 must_be_pod约束
如果一个类具有非平凡的(non-trivial)构造函数、非平凡的拷贝构造函数、非平凡的析构函数,或者非平凡的赋值操作符,那么其对象不能作为联合(union)的成员。
template< typename T >
struct must_be_pod
{
~must_be_pod()
{
void (*p) () = constraints;
}
private:
static void constraints()
{
union
{
T T_is_not_pod_type;
};
T_is_not_pod_type;
}
};
测试代码:
class NonPOD
{
public:
virtual ~NonPOD();
};
must_be_pod<subs> a;
must_be_pod<not_subs> b;
must_be_pod<NonPOD> c; //error
must_be_pod<void> d; //error
这里将void判断为非POD的类型,如果不判断void,那么需要对void做特化,如:
template<>
struct must_be_pod<void>
{
};
1.5 must_be_same_size约束
用来判断两个类型的大小是一致的,代码如下:template< typename T >
struct size_of
{
enum { value = sizeof(T) };
};
template <>
struct size_of<void>
{
enum { value = 0 };
};
template < typename T1,
typename T2>
struct must_be_same_size
{
~must_be_same_size()
{
void (*p) () = constraints;
}
private:
static void constraints()
{
const int T1_must_be_same_size_as_T2
= size_of<T1>::value == size_of<T2>::value;
int i[T1_must_be_same_size_as_T2];
i[0];
}
};
这里使用了size_of来代替sizeof,因为void类型对sizeof非法。特化时要考虑<T,void>,<void,T>和<void,void>,比较繁琐。所以采用了另一种方式。
T1和T2大小不一样时,数组i的定义为int i[0];这显然是编译错误。
测试代码:
must_be_same_size<int, int> e;
must_be_same_size<int, long> f;// depends on OS
must_be_same_size<void, void> g;
must_be_same_size<void, int> h;// error