Don’t fall into common template pitfalls

Posted: August 11, 2011 in Programming Recipes
Tags: ,

[From the reference] Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type.

In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function. These function templates can use these parameters as if they were any other regular type.

We also have the possibility to write class templates, so that a class can have members that use template parameters as types.

I’m not going to write a tutorial about templates. Rather, I’m going to talk about some traps of templates I’ve personally found working with other programmers on a real code.

The first pitfall is about template inheritance. What if a template-derived-class uses a nested type it inherits from its template-base-class? I’m speaking of something like this (more info at Marshall Cline’s FAQs):


 template<typename T>
 class Base {
 public:
      class Abc { ... };
      typedef double myNestedDouble;
 };

 template<typename T>
 class Derived : public Base {
 public:
      void derivedFunction ()
      {
           Abc var;           // not legal
           myNestedDouble y;  // not legal
      }
 };

Last lines produce errors, even though some compilers accept them. Within Derived<T>::derivedFunction(), name Abc and myNestedDouble do not depend on template parameter T, so they are known as nondependent names. On the other hand, Base<T> is dependent on template parameter T so Base<T> is called a dependent name.

The rule is: the compiler does not look in dependent base classes (like Base<T>) when looking up nondependent names (like Abc or myNestedDouble).

A naive solution can be writing last lines in this way:

Base<T>::Abc var;           // still not legal
Base<T>::myNestedDouble y;  // still not legal

This code is bad because those name might not be types. The reason is that there can be a specialization of Base<T>, say Base<Foo>, where Base<Foo>::Abc is a data member, for example. Because of this potential specialization, the compiler cannot assume that Base<T>::Abc is a type until it knows T. The solution is to give the compiler a hint via the typename keyword:

template<typename T>
class Derived : public Base {
public:
     void g()
     {
          typename B<T>::Xyz x; // finally good!
          typename B<T>::Pqr y; // finally good!
     }
};

Same story when a template-derived-class tries to use a member it inherits from its template-base-class:

template<typename T>
class Base {
public:
     void f() { } // member of Base
};

template<typename T>
class Derived : public Base {
public:
     void g()
     {
          f(); // not legal
     }
};

Again,  the compiler does not look in dependent base classes (like Base<T>) when looking up nondependent names (like f). How to screw the compiler over?

  • Insert using Base<T>::f; just prior to calling f().
  • Change the call from f() to B<T>::f(). Note however that this might not give you what you want if f() is virtual, since it inhibits the virtual dispatch mechanism.
  • Change the call from f() to this->f(). Since this is always implicitly dependent in a template, this->f is dependent and the lookup is therefore deferred until the template is actually instantiated, at which point all base classes are considered.
Another danger I’m warning you is about template and friendship. If you want to declare a function template specialization as a friend, how do you do it? Let’s say we have a function template that does SomethingPrivate to the objects it operates on. For example myNameSpace::SomethingPrivate function. Suppose we want to use this function on our class FriendlyClass. We have to make this function friend of FriendlyClass.
The naive attempt can be:
class FriendlyClass {
     void myPrivateFunction();
     friend void myNameSpace::SomethingPrivate(); // legal but rejected by many compilers
};
To befriend a function template specialization, you can choose one of two syntaxes:
friend void myNameSpace::SomethingPrivate();

and

friend void myNameSpace::SomethingPrivate<>();
// or
// friend void myNameSpace::SomethingPrivate<FriendlyClass>();
The first syntax is fragile and rejected by many compilers because it works only if the name is qualified and the indicated class or namespace does not also contain a matching nontemplate function. Then, Sutter suggests to always explicitly add at least the <> template syntax.

The last pique of C++ I’m dealing with concerns template specializations. You can read the complete discussion in Sutter’s Exceptional C++ Style (chapter 7). Consider the following code:
template<class T> void f(T);   // (a) primary template

template<class T> void f(T*);  // (b) primary template that overloads (a)

template<> void f(int*)        // (c) explicit specialization of (b)

// ...

int *p;
f(p);                          // calls (c)
Ok, the last line calls (c) and this is just what you’d expect. Why? If you say “I wrote a specialization for a pointer to int, so obviously that’s what should be called” then I have bad news for you, that’s the wrong reason. Now put the code in the famous form of Dimov/Abrahams:
template<class T> void f(T);   // (a) primary template

template<> void f(int*)        // (c) explicit specialization of (a)

template<class T> void f(T*);  // (b) another primary template that overloads (a)

// ...

int *p;
f(p);                          // which is called?!
It’s better if you sit down. The last line calls the third f (marked as b) ! That’s because overload resolution ignores specializations and operates on the base function templates only. Bear in mind that primary templates win against specializations. I heartly recommend you to read the complete discussion in Sutter’s book! But if you can’t wait, the moral of the story is that if you’re writing a function template, prefer to write it as a single function template that should never be specialized or overloaded, and implement the function template entirely in terms of a class template.
If you’re using someone else’s plain old function template and you want to write your own special-case version that should participate in overloading, don’t make it a specialization: just make it an overload function with the same signature.

I wanted to point these pitfalls out because I recently saw many (expert) programmers being deceived by them, wasting time to figure out how to survive. Next time you’ll employ templates, I hope you’ll be prepared!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s