對于現在的大多數編譯器,中間變量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),不過這往往是一個更好的約束。為約束想一個好名字是很難的。
既然已經有了優秀的qsort()函數,為什么還需要一個sort()?
對于初學者來說,
qsort(array,asize,sizeof(elem),elem_compare);
看上去太古怪了,而且比這個更難理解:
sort(vec.begin(),vec.end());
對于專家來說,在元素與比較方式(comparison criteria)都相同的情況下,sort()比qsort()更快,這是很重要的。而且,qsort()是通用的,所以它可以用于不同容器類型、元素類型、比較方式的任意有意義的組合。舉例來說:
struct Record {
string name;
// ...
};
延伸閱讀
文章來源于領測軟件測試網 http://www.kjueaiud.com/