views:

611

answers:

3

Be forewarned: This question seems way more obvious than it actually is.

I'd like to write a template that can accept any concrete class or template class as a template parameter. This might seem useless because without knowing whether the passed in T is templated or not you won't know how to use it. The reason I want this is so that I can declare a general template with no definition, that users then specialize. Because users are specializing it, they always know about the type they're dealing with. But users can't specialize a template without it first being declared.

You could do this:

template<class T>
class myclass;

But that won't work if you pass in a templated T, for example myclass<std::vector> won't work. So then we try this:

template<class T>
class myclass;

template<template<class> T>
class myclass;

This might be the right track, but it won't work as is because class templates can't be overloaded. So let's switch it to function templates, which can be:

template<class T>
void myfunc();

template<template<class> T>
void myfunc();

Sweet, so we're done right? Well, there might be different numbers of parameters given to the template template parameter, so we'll need to take that into account too.

template<class T>
void myfunc();

template<template<class> T>
void myfunc();

template<template<class, class> T>
void myfunc();

template<template<class, class, class> T>
void myfunc();

// etc.

Ugly, but the Boost Preprocessor Library can generate this code for us (and in C++0x support for variadic templates will be added so this ugliness is only temporary). But we've still forgotten a case! What if one of T's parameters isn't a class, but a constant integer. Lets try to support that:

template<class T>
void myfunc();

template<template<class> T>
void myfunc();

template<template<class, class> T>
void myfunc();

template<template<class, class, class> T>
void myfunc();

// etc.

template<template<class> T>
void myfunc();

template<template<class, int> T>
void myfunc();

template<template<int, class> T>
void myfunc();

template<template<int, class, class> T>
void myfunc();

template<template<class, int, class> T>
void myfunc();

template<template<class, class, int> T>
void myfunc();

// etc.

Uh oh. Given that any constant type can get passed into a template, in any number, mixed with class parameters, KABLOOEY combinatorial explosion. Just to make things more difficult, what if any of T's parameters are themselves templates?

+1  A: 

The solution is: use function overloading and not template specialization! Compile time will be better, constraints gone ...

Remember that any template is considered instantiated at the closest global scope after which they are used. This mean that this will work:

template <class T>
int f(const T& t)
{
  return g(t);
} 

int g(int a)
{
  return a+1;
}

int main()
{
   return f(5);
}

As you see, it works even though the function g is defined after f. This means that you can define whatever template you want that uses the function, as long as the user defines the function before it uses the template, you're fine, C++ will find it.

Remember also that template specialization doesn't exist for functions! If many functions with the same name exist, C++ will always expect overloading, not template specialization.

In short, the best solution is: just use the function as if it existed and expect the users to define the function before they use the template.

PierreBdR
The question already uses overloading - the answer doesn't address the question. As I understand it, he wants to be able to call f<vector>(), f<int>(), f<boost::bind>() (!) etc.
James Hopkin
I disagree ... he wants to define a template for the user to specialize it. Then, the OP suggests predefining functions templates. But you can't specialize function templates. Also, if the OP wants to define this function, this is to use it in his code. Otherwise, the OP should gives more details on how the function will be used.
PierreBdR
+3  A: 

boost::mpl does something like this (here's their idea of binding an argument). However, you have to make a lot of assumptions to make it work, like using;

template <class T> foo { }; typedef foo< int_<4> > my_foo_4;

instead of

template <int T> foo { }; typedef foo<4> my_foo_4;

to not have to offer overloads for all int, char, bool, etc combination.

I can't think of anything that would be more effective than the boost::mpl approach, and in general, I would think any approach would suffer a lot of problems; a class template is NOT a type, and it can't really be wedged into the type system that way (boost::mpl treats it as a function to create new types; more generically, it is used to create a "MetaFunction"). I'm not even sure if variadic templates are going to effect template template parameters (interesting question though).

Todd Gardner
Todd, they will. in C++1x, you will be able to do template<typename> struct Take; template<template<typename...> class T> struct Take<T<stuff>> { }; You may also accept any type and grab their arguments off template<template<typename...> class T, typename... Ts> struct Take<T<Ts...>> { ... };
Johannes Schaub - litb
Using int_<4> is an idea I'd thought of, but I was afraid of going that route ;) I'd have to do the same thing for template template parameters, and for plugging arguments into those, which is getting to be a little more than I can easily visualize. Guess I just need to try it and see what happens.
Joseph Garvin
MPL allows that easily using lambdas: pass vector<_1> as a type, then use typename mpl::apply<T, U>::type (T being the template parameter), and you get vector<U>
Johannes Schaub - litb
A: 
template <class T> class myclass;

template <> class myclass<int>{};
template <> class myclass<std::vector<int>>{};
template <> class myclass<std::vector<std::vector<int> > >{};

Is this what you want?

Dolphin
Nope. I want to be able to specialize myclass on std::vector without actually giving std::vector a type. You need template template parameters for this.
Joseph Garvin