Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 20.  Shims


20.8. Why Not Traits?

We've seen some shims implemented as traits, others can be implemented as functions, and others again as functions that return temporary instances of proxy classes. Throughout parts of this discussion, you may have wondered whether traits [Stro1997] could be used to provide similar generalization facilities to shims. Indeed, some commentators have suggested that shims should only be implemented using traits. There are a number of reasons why the traits technique cannot be the only implementation technique for shims.

First, functions have the capability to deduce type automatically, whereas traits, being template classes, must always be explicitly qualified. This is not only of syntactic significance. It means that traits can only be used in contexts in which type is known.

Second, shims can directly incorporate existing (or new!) C-functions without any C++ function or class wrapping.

Third, traits are not a good conceptual fit for this purpose. A shim concerns itself with only one particular facet, conversion, or manipulation on a (usually) loosely associated range of types. Conversely traits can, and usually do, define and associate a number of characteristics and operations with a (usually) closely related range of types. In this way, the classes and/or functions that constitute a particular shim do not become bloated with accreted unnecessary or unspecific baggage, which is something that (regrettably) happens to traits.

However, the most compelling reason for rejecting an exclusively traits-based implementation is to do with namespaces. If shims were based on traits, we would have two choices for the specialization. We could include the header in which the template definition resided into the header for our code, and then specialize within the main namespace in our own header. This would introduce a great deal of physical coupling [Lako1996], which I hate (and you do too, I'm sure).

Or we could forward declare the template in its original namespace within our header—still defining our specialization in the original namespace—to avoid the coupling. This fails in many ways. One such is that standard library constructs supply type definitions that are compatible with, but may not be identical to, the expected published definitions [Dewh2003]. The construct might be defined within a namespace that is actually defined (directly or indirectly) via a macro; if we did not include any headers within which this macro was defined, we would be specializing a template in the wrong namespace, and hence specializing something that does not exist.


      Previous section   Next section