Nolo Contendere

Alert reader and critic-at-large Terje Slettebø has identified an error in Part 1 of this series of columns, pointing out that the simple code used to demonstrate the “round trip” approach to the typeof implementation was illegal:

const int DoubleVal = 12; // code for double

//…

Int2Int<DoubleVal> typeCode(double &);

//…

template <int typecode> struct DeCode;

template <> struct DeCode<DoubleVal>

   { typedef double R;};

//…

DeCode<typeCode(cont[0]).value>::R // illegal!

value( cont[0] );

The argument used to instantiate the DeCode template must be an integer constant-expression, and a constant-expression may not contain, directly, a function call.  My compilers (and, admittedly, I) had a more liberal and non-standard view of the matter, however, and allowed the expansion because it was possible in this case to determine the value of the expression at compile time.  In situations like this, we usually reach for sizeof:

char (*typeCode(double &))[DoubleVal];

//…

typename DeCode<sizeof(*typeCode(cont[0]))>::R // OK

value( cont[0] );

In this modified version, the typeCode functions return a pointer to an array of characters, where the array bound is a compile time constant equal to the code of interest.  It is legal to employ sizeof in an integer constant-expression, even if the sizeof expression contains a function call.  Therefore we can extract the code as the sizeof the dereferenced return type of the function.  This was the approach we took in the implementation of the genGN functions in Part 2 of this series.