The world of object-oriented programming is centered around explicit interfaces (explicit interfaces) and runtime polymorphism (execution phase polymorphism. For example, the following (meaningless) class (class) is given ).
class Widget {
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other); // see Item 25
...
};
And this (also meaningless) function ),
void doProcessing(Widget& w)
{
if (w.size() > 10 && w != someNastyWidget) {
Widget temp(w);
temp.normalize();
temp.swap(w);
}
}
We can talk about w in doProcessing as follows:
· Because w is declared as a reference of the Widget type, w must support the Widget interface (interface ). We can find this interface (interface) in the source code (for example, the Widget's. h file) to see what it looks like, so we call it an explicit interface-it is explicitly visible in the source code.
· Because some member functions (member functions) of widgets are virtual, w calls to these functions as runtime polymorphism (execution phase polymorphism ): the specific function to be called is determined based on the dynamic type (dynamic type) of w During execution (see 《C ++ proverbs: Do not redefine the inherited non-virtual functions).
The world of templates and generic programming is fundamentally different. In that world, explicit interfaces and runtime polymorphism continue to exist, but they are less important. As an alternative, implicit interfaces (implicit Interface) and compile-time polymorphism (polymorphism during compilation) are pushed to the front. To understand how this works, let's take a look at what happens when we convert doProcessing from a function to a function template:
template
void doProcessing(T& w)
{
if (w.size() > 10 && w != someNastyWidget) {
T temp(w);
temp.normalize();
temp.swap(w);
}
}
How can we talk about w in doProcessing?
· The interfaces required by w are determined by the operations performed on w in the template. In this example, the type (T) of w must support size, normalize, and swap member functions (member function); copy construction (copy constructor) (used to create temp), and comparison of not equal to (used to compare with someNastyWidget ). We will see that this is not very accurate in the future, but it is correct enough for now. It is important that this series of expressions that must be effectively applicable to template compilation are the implicit interface (implicit interface) that T must support ).
· Operator> and operator! = Such calls to functions containing w may be accompanied by instantiating templates (instantiation templates) to make these calls successful. Such instantiation occurs during compilation. Because function templates is instantiated using different template parameters (template parameters), different functions are called. Therefore, it is known as compile-time polymorphism (compilation phase polymorphism.
Even if you have never used a template, you should be familiar with the differences between runtime and compile-time polymorphism, it is similar to the dynamic binding (dynamic binding) that determines which of the following overload functions should be called (this occurs during compilation) and the virtual function (virtual function) Call) (This occurs at runtime. The difference between explicit and implicit interfaces is the new content related to the template, which needs to be closely investigated.
An explicit interface is composed of function signatures (function Recognition Feature), that is, the function name, parameter type, return value, and so on. For example, the public interface (explicit interface) of the Widget class (class ),
class Widget {
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other);
};
It consists of a constructor, A destructor, and a function size, normalize, and swap. In addition, parameter types (parameter type) and return types (return type) and the constnesses (constants) of these functions ). (It also includes copy constructor (copy constructor) and copy assignment operator (copy assignment operator) of compiler-generated (generated by the compiler)-see 《C ++ proverbs: Understand What C ++ secretly adds and callsIt may also contain typedefs, and if you are brave enough to violate the suggestion of making data members (data member) private (private), even in this case, these data members (data members) are not.
An implicit interface (implicit interface) is very different. It is not based on function signatures (function Recognition Feature. It is composed of valid expressions (legal expression. Let's look at the conditions at the beginning of doProcessing template:
template
void doProcessing(T& w)
{
if (w.size() > 10 && w != someNastyWidget) {
...
Implicit interface (implicit interface) of T (w type) seems to have the following constraints:
· It must provide a member function (member function) that returns a positive value named size ).
· It must support an operator used to compare two types of T objects! = Function. (Here, we assume that the type of someNastyWidget is T .)
Because of the possibility of operator overloading (operator overloading), these two constraints do not have to be met. Yes, T must support a size member function (member function). It is worth mentioning that although this function can be inherited from a base class (base class. However, this member function does not need to return an integer. It does not even need to return a value type. In this case, it does not even need to return a type that defines operator>! All it needs to do is to return an object (object) of Type X. There is an operator> you can use an object of Type X (object) and an int (because 10 is of the int type). This operator> does not need to get a parameter of the Type X, because it can get a parameter of the Type Y, as long as it is in the objects (object) of the Type X and the objects (object) of the Type Y) there is an implicit conversion (implicit transformation) between them!
Similarly, T supports operator! = Is unnecessary, because if operator! = It is acceptable to obtain an object of Type X and an object of Type Y. As long as T can be transformed into X, and someNastyWidget type can be transformed into Y, for operator! = Is valid.
(A side note: The analysis here does not consider the possibility of operator & being overloaded. This will change the meaning of the above expression from and to something totally different .)
Most people have headaches when considering implicit interfaces (implicit Interface) for the first time, but they really don't need aspirin. Implicit interfaces (implicit Interface) is simply composed of a set of valid expressions. These expressions may seem complicated, but the constraints they apply are generally easy to understand. For example, this condition is given,
if (w.size() > 10 && w != someNastyWidget) ...
About functions size, operator>, operator & or operator! = Constraints on can hardly tell more things, but it is very easy to identify the constraints of the entire expression. The condition part of an if statement must be a boolean expression (boolean expression! = SomeNastyWidget "refers to the exact type involved in the generated type. It must be compatible with bool. This is part of the implicit interface (implicit interface) That template (template) doProcessing applies to its type parameter (type parameter) T. The rest of interfaces (interfaces) required by doProcessing are copy constructor (copy constructor). The call of normalize and swap must be valid for objects (objects) of type T.
The Influence of implicit interface (implicit interface) on parameters (parameters) of template (template) is just like the objects (object) of explicit interfaces (explicit interface) on a class (class) and both are checked during compilation. Just as you cannot use an object (object) (Code cannot be compiled) in a way that contradicts the explicit interface provided by its class (class, unless an object (object) supports the implicit interface (implicit interface) required by the template, you cannot try to use this object (object) in a template) (The Code still cannot be compiled ).
Things to Remember
· Both classes and templates support interfaces and polymorphism ).
· For classes (classes), interfaces (interfaces) are explicit and centered on function signatures (function Recognition features. Polymorphism (polymorphism) occurs at runtime through virtual functions (virtual function.
· For template parameters (template parameter), interfaces (Interface) is implicit (implicit) and is based on valid expressions (legal expression ). Polymorphism (polymorphism) occurs during compilation through template instantiation and function overloading resolution (function overload parsing.