Archive for January, 2008

Quick reference guide for template declarations

Tuesday, January 29th, 2008

Function templates by example

Your basic template declaration. (No angle brackets after the template name.)

template< class T, class U > T* just_template( U );

Declaration of explicit (full) specialization:

template<> char* just_template< char, int >( int );

Explicit instantiation. (No angle brackets after the template keyword.)

template const char* just_template< const char, double >( double );

Class templates by example

Basic declaration:

template< class T > class YetAnotherTemplate;

Declaration of partial specialization (e.g. for pointer types):

template< class T > class YetAnotherTemplate< T* >;

Declaration of full specialization (for long):

template<> class YetAnotherTemplate< long >;

Explicit instantiation:

template class YetAnotherTemplate< float >;

Notes
You can’t partially specialize function templates. This is a good thing(tm) in my opinion. For a better explanation than I can give, see Sutter’s Mill.

Partial specialization doesn’t necessarily mean fewer template parameters, it really does just mean more specialized.

For a function template specialization you can omit any trailing template arguments that can be deduced; if all template arguments can be deduced you can just use the unadorned template name in the declaration of the specialization.

Similarly, for explicit instantiations of function templates you can just use the template name where the arguments can be deduced.

e.g.

template< class T > void f( T );

template f( int ); // Explicit instantiation

template<> f( double ); // Declaration of explicit specialization

Caution required with integer conversions

Wednesday, January 2nd, 2008

This applies to both C and C++, although the details are a little different in the more obscure corners. Consider this C++ snippet which is designed to position a streambuf somewhere a bit before its end:

pos = sb->pubseekoff( -128 - thing.GetSize(), ios_base::end, ios_base::in );

How did this fail spectacularly?

OK, here are the types.

sb is a std::streambuf
thing.GetSize() returns an unsigned int
pubseekoff takes a streambuf::off_type which is some sort of signed type for representing stream offsets.

Here’s what happened on my implementation.

128 is an integer constant, and fits in an int, so it’s an int.
After integral promotions, it’s still an int (phew!).

-128 is, therefore an int.

thing.GetSize() is an unsigned int, so -128 gets converted to an unsigned int for the binary ‘-’ operation. (Uh-oh!)

Finally this unsigned int gets converted into whatever streambuf::off_type is. If off_type behaved like a signed int then (implementation defined!) you might get the conversion to wrap around back to the negative numbers to a value that you were expecting in the first place.

On the other hand, off_type might act like a 64-bit signed integer type, with unsigned int being a 32-bit unsigned type. In this case, the very large positive unsigned 32-bit number that you were hoping was actually a reasonably small negative number will quite happily stay large and positive in the conversion. The function call then ends up trying to seek 3.99 GBytes beyond the end of the stream. Whoops.