December 12, 2002
Named Template Arguments
Chapter 16, Templates and Inheritance of C++ Templates discusses a technique to provide a sort of named template arguments. Two people noted that simpler alternatives exists.
The book presents code that allows us to write:
BreadSlicer<Policy3_is<CustomPolicy> > b1;
            Policy3_is<AnotherPolicy> > b2;
In other words, the template arguments (presumably policy classes in the example above) can be provided in any order, provided they are wrapped in a template like Policy3_is that indicates which argument is intended.

The technique we present to enable this requires a few dozen lines of slightly subtle, somewhat boring code.

However, there are alternatives. Consider the following code:

struct Defaults {
  typedef double ElementT;
  static void move(char*, char*);

template<typename OtherProps = Defaults>
struct Mover: OtherProps {
  static void move(char*, char*);

template<typename T, typename OtherProps = Defaults>
struct ElementType: OtherProps {
  typedef T ElementT;

template<typename Props = Defaults>
struct WonderData {
  // ...

void my_code() {
  WonderData<ElementType<long, Mover<> > > data;
  // ...
In this code, every policy or property is a template whose last (and sometimes: only) parameter is meant to be substituted by other parameters. The default for this trailing parameter is a class type Defaults that encapsulates the default properties or policies.

This works and is in some ways considerably simpler than the named template arguments technique presented in our book. This simplicity comes with a different set of compromises, of course.

First, it is syntactically somewhat asymetric, even though semantically it should not be so. Indeed, ElementType<long, Mover<> > looks quite different from Mover<ElementType<long> >, but in fact these two are intended to mean the same thing.

Second, the burden is not put on the writer of the policy or property templates instead of on the writer of the main template. This is not necessarily a disadvantage (especially since that burden is smaller in complexity). However, we prefer to move complexity from client code to the library on the premise that there will be many instances of client code for a single library.

Finally, as written above, the inheritance relationship offers opportunities for unintended clashes between properties or policies, but that can be avoided by adding a typedef member for the actual property or policy (much as is done in our named template arguments technique).

Posted by Daveed at 09:35 PM | Comments (0)
Post a comment

Email Address:



Remember info?