Imperfect C++ 读书笔记(一):约束

这里主要讲述编译期的强制,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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值