Writing a Member Function Template
Problem
You have a single member function that needs to take a parameter that can be of any type, and you can't or don't want to be constrained to a particular type or category of types (by using a base class pointer parameter).
Solution
Use a member function template and declare a template parameter for the type of object the function parameter is supposed to have. See Example 8-13 for a short example.
Example 8-13. Using a member function template
class ObjectManager { public: template T* gimmeAnObject( ); template void gimmeAnObject(T*& p); }; template T* ObjectManager::gimmeAnObject( ) { return(new T); } template void ObjectManager::gimmeAnObject(T*& p) { p = new T; } class X { /* ... */ }; class Y { /* ... */ }; int main( ) { ObjectManager om; X* p1 = om.gimmeAnObject( ); // You have to specify the template Y* p2 = om.gimmeAnObject( ); // parameter om.gimmeAnObject(p1); // Not here, though, since the compiler can om.gimmeAnObject(p2); // deduce T from the arguments }
Discussion
When talking about function or class templates, the words parameter and argument have some ambiguity. There are two kinds of each: template and function. Template parameters are the parameters in the angle brackets, e.g., T in Example 8-13, and function parameters are parameters in the conventional sense.
Consider the ObjectManager class in Example 8-13. It is a simplistic version of the Factory pattern discussed in Recipe 8.2, so I have defined the member function gimmeAnObject as something that creates new objects that client code would use instead of calling new directly. I can do this by either returning a pointer to a new object or by modifying a pointer passed in by the client code. Let's take a look at each approach.
Declaration of a template member function requires that you provide the template keyword and the template parameters.
template T* gimmeAnObject( ); template void gimmeAnObject(T*& p);
Both of these member functions happen to use T as the template parameter, but they don't need to; they each represent the template parameter for that member function only, so the names are unrelated. You have to do the same thing for your template member function definition, i.e., use the template keyword and list all the template parameters. Here's what my definitions look like:
template T* ObjectManager::gimmeAnObject( ) { return(new T); } template void ObjectManager::gimmeAnObject(T*& p) { p = new T; }
There are a couple of ways to call template member functions. First, you can invoke them with explicit use of the template parameter, like this:
X* p1 = om.gimmeAnObject( );
X is just some class name. Or, you can let the compiler deduce the arguments for the template parameters by passing in arguments of the type(s) of the template parameters. For example, you can invoke the second form of gimmeAnObject without passing in anything in angle brackets:
om.gimmeAnObject(p1);
This is because the compiler can deduce T by looking at p1 and recognizing that it's an X*. Template deduction only works for function templates (member or not) and only works when the template parameters are deduced from the function arguments.
Member function templates aren't the most popular feature in C++, but they come in handy from time to time, so it's good to know how to write one. I often see the need crop up when I want a member function to work for types that are not related by inheritance. For example, if I have a member function foo that I want to take a single argument that is always going to be a class that inherits from some base class, I don't need a template: I can just make the parameter type a base class pointer or reference. Then, any objects of subclasses of the parameter class will work just finesuch is the way of C++.
But you may want a function that operates on parameters that don't all inherit from the same base class(es). In this case, you can either write the same member function several timesonce for each typeor make it a template member function. Using templates also permits specialization, which is a way of providing implementations of templates for particular template arguments. But that's beyond the scope of a single recipe, so I won't discuss it further here, but it's a powerful technique, so if template programming appeals to you, I encourage you check it out.
See Also
Recipe 8.11