C++中,能不能为模板参数定义约束
可以的,而且方法非常简单和通用。看看这个:
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”这个词(给用户一个线索),约束的名字,以及导致约束失败的详细错误(例如“无法用double*初始化Shape*”)。
我并不认为Can_copy()已经可以标准化了——它需要更多的使用。同样,在C++社区中,各种不同的约束方式被使用;到底是哪一种约束模板在广泛的使用中被证明是最有效的,还没有达成一致的意见。
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*”)。
我并不认为Can_copy()已经可以标准化了——它需要更多的使用。同样,在C++社区中,各种不同的约束方式被使用;到底是哪一种约束模板在广泛的使用中被证明是最有效的,还没有达成一致的意见。