快捷搜索:   服务器  安全  linux 安全  MYSQL  dedecms

Bjarne:为什么不能为模板参数定义约束?

    可以的,而且方法非常简单和通用。

  看看这个:

 template<class container>

void draw_all(container& c)

{

 for_each(c.begin(),c.end(),mem_fun(&shape::draw));

}


  如果出现类型错误,可能是发生在相当复杂的for_each()调用时。例如,如果容器的元素类型是int,我们将得到一个和for_each()相关的含义模糊的错误(因为不能够对对一个int值调用shape::draw的方法)。

  为了提前捕捉这个错误,我这样写:

 template<class container>

void draw_all(container& c)

{

 shape* p = c.front(); // accept only containers of shape*s
 for_each(c.begin(),c.end(),mem_fun(&shape::draw));

}

  对于现在的大多数编译器,中间变量p的初始化将会触发一个易于了解的错误。这个窍门在很多语言中都是通用的,而且在所有的标准创建中都必须这样做。在成品的代码中,我也许可以这样写:

 template<class container>

void draw_all(container& c)

{

 typedef typename container::value_type t;

 can_copy<t,shape*>(); // accept containers of only shape*s
 for_each(c.begin(),c.end(),mem_fun(&shape::draw));

}

  这样就很清楚了,我在建立一个断言(assertion)。can_copy模板可以这样定义:

 template<class t1, class t2> struct can_copy {

 static void constraints(t1 a, t2 b) { t2 c = a; b = a; }

 can_copy() { void(*p)(t1,t2) = constraints; }

};

  can_copy(在运行时)检查t1是否可以被赋值给t2。can_copy<t,shape*>检查t是否是shape*类型,或者是一个指向由shape类公共继承而来的类的对象的指针,或者是被用户转换到shape*类型的某个类型。注意这个定义被精简到了最小:

  一行命名要检查的约束,和要检查的类型

  一行列出指定的要检查的约束(constraints()函数)

  一行提供触发检查的方法(通过构造函数)

  注意这个定义有相当合理的性质:

  你可以表达一个约束,而不用声明或复制变量,因此约束的编写者可以用不着去设想变量如何被初始化,对象是否能够被复制,被销毁,以及诸如此类的事情。(当然,约束要检查这些属性的情况时例外。)

  使用现在的编译器,不需要为约束产生代码

  定义和使用约束,不需要使用宏

  当约束失败时,编译器会给出可接受的错误信息,包括“constraints”这个词(给用户一个线索),约束的名字,以及导致约束失败的详细错误(例如“无法用double*初始化shape*”)。

  那么,在c++语言中,有没有类似于can_copy——或者更好——的东西呢?在《c++语言的设计和演变》中,对于在c++中实现这种通用约束的困难进行了分析。从那以来,出现了很多方法,来让约束类变得更加容易编写,同时仍然能触发良好的错误信息。例如,我信任我在can_copy中使用的函数指针的方式,它源自alex stepanov和jeremy siek。我并不认为can_copy()已经可以标准化了——它需要更多的使用。同样,在c++社区中,各种不同的约束方式被使用;到底是哪一种约束模板在广泛的使用中被证明是最有效的,还没有达成一致的意见。

  但是,这种方式非常普遍,比语言提供的专门用于约束检查的机制更加普遍。无论如何,当我们编写一个模板时,我们拥有了c++提供的最丰富的表达力量。看看这个:

 template<class t, class b> struct derived_from {

 static void constraints(t* p) { b* pb = p; }

 derived_from() { void(*p)(t*) = constraints; }

};

template<class t1, class t2> struct can_copy {

 static void constraints(t1 a, t2 b) { t2 c = a; b = a; }

 can_copy() { void(*p)(t1,t2) = constraints; }

};

template<class t1, class t2 = t1> struct can_compare {
 static void constraints(t1 a, t2 b) { a==b; a!=b; a<b; }
 can_compare() { void(*p)(t1,t2) = constraints; }
};

template<class t1, class t2, class t3 = t1> struct can_multiply {

 static void constraints(t1 a, t2 b, t3 c) { c = a*b; }

 can_multiply() { void(*p)(t1,t2,t3) = constraints; }

};


 

 struct b { };

struct d : b { };

struct dd : d { };

struct x { };

int main()

{

 derived_from<d,b>();

 derived_from<dd,b>();

 derived_from<x,b>();

 derived_from<int,b>();

 derived_from<x,int>();

 can_compare<int,float>();

 can_compare<x,b>();

 can_multiply<int,float>();

 can_multiply<int,float,double>();

 can_multiply<b,x>();

 can_copy<d*,b*>();

 can_copy<d,b*>();

 can_copy<int,b*>();

}

// 典型的“元素必须继承自mybase*”约束:

template<class t> class container : derived_from<t,mybase> {

// ...

};

  事实上,derived_from并不检查来源(derivation),而仅仅检查转换(conversion),不过这往往是一个更好的约束。为约束想一个好名字是很难的。

顶(0)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论