1、The initial motivation for C++ templates was straightforward: to make it possible to create type-safe containers like vector, list, and map. Ultimately, it was discovered that the C++ template mechanism is itself Turing-complete: it can be used to compute any computable value. That led to template metaprogramming: the creation of programs that execute inside C++ compilers and that stop running when compilation is complete.
This chapter won‘t make you an expert template programmer, but it will make you a better one. It will also give you information you need to expand your template-programming boundaries as far as you desire.
<Item 41> Understand implicit interfaces and compile-time polymorphism
2、The world of object-oriented programming revolves around explicit interfaces and runtime polymorphism.The world of templates and generic programming is fundamentally different. In that world, explicit interfaces and runtime polymorphism continue to exist, but they‘re less important. Instead, implicit interfaces and compile-time polymorphism move to the fore.
3、如下代码意味着可以描述w如下两点
- The interface that w must support is determined by the operations performed on w in the template. In this example, it appears that w‘s type (T) must support the size, normalize, and swap member functions; copy construction (to create temp); and comparison for inequality (for comparison with someNastyWidget). We‘ll soon see that this isn‘t quite accurate, but it‘s true enough for now. What‘s important is that the set of expressions that must be valid in order for the template to compile is the implicit interface that T must support.
- The calls to functions involving w such as operator> and operator!= may involve instantiating templates to make these calls succeed. Such instantiation occurs during compilation. Because instantiating function templates with different template parameters leads to different functions being called, this is known as compile-time polymorphism.
template<typename T> void doProcessing(T& w) { if (w.size() > 10 && w != someNastyWidget) { T temp(w); temp.normalize(); temp.swap(w); } }
4、An explicit interface typically consists of function signatures, i.e., function names, parameter types, return types, etc.An implicit interface is quite different. It is not based on function signatures. Rather, it consists of valid expressions. Look again at the conditional at the beginning of the doProcessing template:
template<typename T> void doProcessing(T& w) { if (w.size() > 10 && w != someNastyWidget) { ... }
The implicit interface for T (w‘s type) appears to have these constraints:
- It must offer a member function named size that returns an integral value.
- It must support an operator!= function that compares two objects of type T. (Here, we assume that someNastyWidget is of type T.)
Thanks to the possibility of operator overloading, neither of these constraints need be satisfied. Yes, T must support a size member function, though it‘s worth mentioning that the function might be inherited from a base class. But this member function need not return an integral type. It need not even return a numeric type. For that matter, it need not even return a type for which operator> is defined! All it needs to do is return an object of some type X such that there is an operator> that can be called with an object of type X and an int (because 10 is of type int). The operator> need not take a parameter of type X, because it could take a parameter of type Y, and that would be okay as long as there were an implicit conversion from objects of type X to objects of type Y!
Similarly, there is no requirement that T support operator!=, because it would be just as acceptable for operator!= to take one object of type X and one object of type Y. As long as T can be converted to X and someNastyWidget‘s type can be converted to Y, the call to operator!= would be valid.
(As an aside, this analysis doesn‘t take into account the possibility that operator&& could be overloaded, thus changing the meaning of the above expression from a conjunction to something potentially quite different.)
5、The implicit interfaces imposed on a template‘s parameters are just as real as the explicit interfaces imposed on a class‘s objects, and both are checked during compilation.
6、Things to Remember
- Both classes and templates support interfaces and polymorphism.
- For classes, interfaces are explicit and centered on function signatures. Polymorphism occurs at runtime through virtual functions.
- For template parameters, interfaces are implicit and based on valid expressions. Polymorphism occurs during compilation through template instantiation and function overloading resolution.
2、