为什么不能为模板参数定义约束(constraints)?


  
可以的,而且方法非常简单和通用。 
  
看看这个: 
  
        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),
不过这往往是一个更好的约束。为约束想一个好名字是很难的。 

posted on 2007-03-24 09:49 阿刚 阅读(236) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2007年3月>
25262728123
45678910
11121314151617
18192021222324
25262728293031
1234567

统计

常用链接

留言簿(1)

随笔档案

文章档案

C++ BBS

C++ FAQ

C++ WEBSITE

搜索

最新随笔

最新评论

阅读排行榜

评论排行榜