1

I am teaching myself template programming in C++, so some of my assumptions may be wrong - please correct me if you see any errors.

I am trying to use an STL list as a template parameter to a function. The function is supposed to be used with all sorts of data types, so I have defined the function as template<class T> rather than template<template<> class T> in its original declaration. I now want to specialise it to support a template class.

template<class T> 
void function(T param)
{
  // do something with T
}

template<template <class T, class Allocator> class listPlaceholder> 
void function(std::list<T, Allocator> param)
{
  // do something with every element in param (I actually need to know it's a list)
  std::list<T, Allocator>::iterator current = param.begin();
  std::list<T, Allocator>::iterator end = param.end();

  do {
    function<T>(*current);
  } while (++current != end);
}

The problem is that when I try to compile this code (under GCC) it says that T and Allocator are not defined in the scope. My primary question is "how do I specialise for template classes?" and secondly, if it is possible, "how do I extract the template template parameters?".

As mentioned previously, I am learning template programming, so obvious solutions are welcome.

3 Answers3

3

You want to declare those parameters

template<template <class T, class Allocator> class listPlaceholder, 
         class T, class Allocator> 
void function(listPlaceholder<T, Allocator> param)
{
  // do something with every element in param (I actually need to know it's a list)
  typename listPlaceholder<T, Allocator>::iterator current = param.begin();
  typename listPlaceholder<T, Allocator>::iterator end = param.end();

  do {
    function<T>(*current);
  } while (++current != end);
}

The names you used in the formal parameter list have no meaning. You also forgot to use listPlaceholder actually. But I'm assuming that was accidental.

As another poster said, you also need the typename keyword because the names are dependent names.

For why the names in the formal list are meaningless, compare it to function pointers:

void f(void (*p)(int t, int allocator), int t, int allocator) {
  p(t, allocator);
}

void g(int a, int b) { 
}

int main() {
  f(&g, 0, 1);
}

What is important is only the type of the parameters, and I could have written void(*p)(int, int) too. In your case, what is important is only that both parameters are type parameters. So you could have written the template template parameter also as template<class, class> class listPlaceholder too, completely equivalent.

Last but not least, I would like to emphasize that you have not specialized function, but you have overloaded it with another template. So, both functions are two completely different function templates.

Community
  • 1
  • 1
Johannes Schaub - litb
  • 466,055
  • 116
  • 851
  • 1,175
2

g++ is actually correct here; you haven't declared T or Allocator in this scope. The template declaration you have

template<template <class T, class Allocator> class listPlaceholder> 
    void function(std::list<T, Allocator> param)

Says "I am parameterized over a class template that takes in two classes as arguments." However, the names of those arguments can't be accessed anywhere in the body of the template. They're mostly there as placeholders, and the above template declaration is equivalent to

template<template <class, class> class listPlaceholder> 
    void function(std::list<T, Allocator> param)

This is similar to how if you were to declare a regular C++ function that took another function as an argument, you can't access the names of the parameters. For example, this is illegal:

void DoSomething(void function(int x, int y)) {
    x = 5; // Error!
}

Since it's equivalent to

void DoSomething(void function(int, int)) {
    x = 5; // Error!
}

I believe what you want to do is change your template function signature to look like this:

template<class T, class Allocator> 
    void function(std::list<T, Allocator> param)

This says "This function is parameterized over two types. When provided as an argument a std::list parameterized over a type and an allocator, the body of this function can refer to those types as T and Allocator."

templatetypedef
  • 328,018
  • 92
  • 813
  • 992
  • 1
    Thanks, this explains the problem well. Does this mean that I will have to re-declare the original function with no specialisation but with these extra template parameters, then specialise the 2 template function into the list? How would you go about achieving the same result with a 1 template class, without it being confused with the original function? – fuseinabowl Jul 24 '11 at 19:03
  • If you wanted to have a generic "something other than list" function and a more specialized "only for lists" function, then you would write the first version as a template function that takes in some type `T` and the second function as above. C++ will always pick the more specialized function during overload resolution. – templatetypedef Jul 24 '11 at 19:04
  • Thank you for your help, it was very useful :) – fuseinabowl Jul 25 '11 at 08:03
0

Use typename as:

typename std::list<T, Allocator>::iterator current = param.begin();
typename std::list<T, Allocator>::iterator end = param.end();

Its because iterator is dependent name, so typename is required by the compiler, so that it can know that iterator is actually a type, not a static value.

To know this in detail, read this FAQ:

Beside, you should be writing your function template as:

template <class T, class Allocator>
void function(std::list<T, Allocator> param)
{
   //code..
}
Community
  • 1
  • 1
Nawaz
  • 327,095
  • 105
  • 629
  • 812