[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.
class FriendlyClass { void myPrivateFunction(); friend void myNameSpace::SomethingPrivate(); // legal but rejected by many compilers };
friend void myNameSpace::SomethingPrivate();
and
friend void myNameSpace::SomethingPrivate<>(); // or // friend void myNameSpace::SomethingPrivate<FriendlyClass>();
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)
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?!
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!