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

2014-11-24 09:25:37 · 作者: · 浏览: 0

这里主要讲述编译期的强制,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 
  
   
 struct must_have_base
   
     { ~must_have_base() { const int not_the_same_type = 0; int i[not_the_same_type]; } }; template 
    
      struct must_have_base
     
       { ~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
  
    oBA;
	must_have_base
   
     oCA; //error must_have_base
    
      oDA; //error must_have_base
     
       AVoid; //error must_have_base
      
        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
  
    oInt;
	must_be_subscriptable
   
     oIntPointer; must_be_subscriptable
    
      oSubs; must_be_subscriptable
     
       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
  
    oPSubs;
	must_be_subscriptable_as_decayable_pointer
   
     oRSubs; //error must_be_subscriptable_as_decayable_pointer
    
      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
  
    a;
must_be_pod
   
     b; must_be_pod
    
      c; //error must_be_pod
     
       d; //error
     
    
   
  

这里将void判断为非POD的类型,如果不判断void,那么需要对void做特化,如:
template<>
struct must_be_pod
  
   
{
};
  

1.5 must_be_same_size约束

用来判断两个类型的大小是一致的,代码如下:
template< typename T >
struct size_of
{
	enum { value = sizeof(T) };
};

template <>
struct size_of
  
   
{
	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
   
    ::value == size_of
    
     ::value; int i[T1_must_be_same_size_as_T2]; i[0]; } };
    
   
  

这里使用了size_of来代替sizeof,因为void类型对sizeof非法。特化时要考虑 ,比较繁琐。所以采用了另一种方式。 T1和T2大小不一样时,数组i的定义为int i[0];这显然是