2.2.3. Genericity versus classification

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.

Table of contents


Marc Girod
Last modified: Sat Feb 28 14:25:31 EET 1998