We distinguished in 2.2.2. two kinds of interfaces, both of which
we described in general terms.
The first kind can easily be expressed through classification: the
interfaces describe operations in their commonalities, all of their
specificities being abstracted away. These interfaces are, on the one
hand, the ones considered last (in c), between operations of arbitrary
types, and on the other hand, the interfaces between operations and
their initiators. The latter are needed to access the higher-level
operations for comparison purposes, walking up through the composition
chains.
The second kind of interfaces, considered in point (b), can
however not be expressed through classification. Although generic,
since applicable to most pairs of operations and initiators, they are
specific in their details. To elicit for some common handling through
classification based polymorphism, classes need to be ready to locally
loose part of their specificity. If this is not appropriate, the
processing must be expressed through genericity (templates).
An example can again be taken from our zoo: we saw that we cannot
afford to drop the information about the specific kind of animals
concerned, when it comes to apply the generic feeding procedure to
them.
The relation by which an operation can report the identity of its initiator follows the same pattern. It may be defined within the abstract operation class GetInput by the member:
Initiator<GetInput>& initiator() const = 0;
This definition presents a hidden problem in the C++ syntax in
which it is expressed: the tension between its genericity, which would
elicit for making it the member of a template class, specialized with
the class parameter GetInput, and its specific belonging to the class
GetInput itself.
This tension may be resolved in first approximation by using the following technique (validated in the draft standard and used in the literature, e.g. in [Stroustrup94]):
class GetInput: virtual public Operation<GetInput> {
//...
};
Beside looking artificial, this technique does not solve every
problem related to the above mentioned tension.
We might for example want to dedicate part of GetInput's interface
to Initiator<GetInput>
through friendship. This
class is, however, not the one we actually mean, unless we provide a
user specialization of it. Anyway, we cannot generically express this
relation! Adding the following declaration within Operation:
friend class Initiator<T>;
does unfortunately not fulfil our need: the relationship implied
by this declaration would bind the two generic parts of the GetInput
operation and initiator, whereas our need would be to -generically!-
bind their respective specific parts.
The existence of similar needs may actually be felt as disclosing
a lack of orthogonality in C++ -but we know of no language in which
this would not be the case- between classification and genericity. In
C++, templates always introduce a new scope, instead of adopting
(after validation) the one in which they are being used. The idea of
validating an existing (class) scope may be compared to that of
computing signatures [BR95]. Signatures might actually be considered
an attempt to implement constrained genericity, more than type
abstraction.