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);
} whi开发者_如何学JAVAle (++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.
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 function
s are two completely different function templates.
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
."
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:
- Where and why do I have to put the "template" and "typename" keywords?
Beside, you should be writing your function template as:
template <class T, class Allocator>
void function(std::list<T, Allocator> param)
{
//code..
}
精彩评论