26

Is it possible to have multiple versions of the same class which differ only in the number of template arguments they take?

For instance:

template<typename T>
class Blah {
public:
    void operator()(T);
};

template<typename T, typename T2>
class Blah {
public:
    void operator()(T, T2);
};

I'm trying to model functor type things which can take a variable number of arguments (up to the number of different templates that were written out).

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • I'm wondering if you could do something with typelists. – john Aug 08 '11 at 21:18
  • @john that would be a good idea but I'm using MSVC++ 2010 which doesn't support them I don't believe. – Seth Carnegie Aug 08 '11 at 21:19
  • Actually boost::tuple is probably a more accessible version of the same idea. Have a generic template with one type, then specialised version with tuples for two, three, four ... types. – john Aug 08 '11 at 21:24

3 Answers3

28

The simplest answer would be to have just one template, with the maximum number you want to support and use void for a default type on all but the first type. Then you can use a partial specialization as needed:

template<typename T1, typename T2=void>
struct foo {
    void operator()(T1, T2);
};

template <typename T1>
struct foo<T1, void> {
     void operator()(T1);
};

int main() {
   foo<int> test1;
   foo<int,int> test2;
   test1(0);
   test2(1,1);
}
Flexo
  • 87,323
  • 22
  • 191
  • 272
  • 1
    I don't think the definition of `operator()(T, T2, T3, T4)` would work right if some of the types were `void`. – Ben Voigt Aug 08 '11 at 21:13
  • 2
    @ben - It doesn't work like that, hence the partial specialization which effectively gives the "overloading" on number of template arguments – Flexo Aug 08 '11 at 21:16
  • 1
    ok thanks, with the partial template specialization trick it works. Thanks very much, I'll mark this as the answer in 6 minutes. – Seth Carnegie Aug 08 '11 at 21:16
  • 1
    @awoodland: This looks good. Much better than your answer when I commented on it. – Ben Voigt Aug 08 '11 at 21:19
  • @Nikolai: The asker of the question states in a comment that he was using MSVC++ 2010, which [doesn't support them](http://msdn.microsoft.com/en-us/library/hh567368.aspx). That said, it could be a valid and useful answer if you want to add it. – icabod Dec 04 '13 at 10:36
  • Is this an example of SFINAE? Meaning: instantiating the base template definition would not compile because `T2` can't actually be `void`. Is this what's going on here? – Aviv Cohn Jul 07 '20 at 14:09
  • No, it's just specialisation and default template arguments. – Flexo Jul 07 '20 at 18:39
22

A template can have only one base definition. If you need a variable number of arguments and you don't want to use "null type" constructions as @awoodland suggests, and if you have a C++0x compiler, then you can use variadic templates:

template <typename ...Dummy> struct foo; // base case, never instantiated!

template <typename T> struct foo<T> { /*...*/ };  // partial spec. for one parameter
template <typename T, typename U> struct foo<T, U> { /*...*/ };  // ditto for two
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
0

This is untested code, I don't have a version of boost handy, but here goes anyway

#include "boost/tuple.h"

template <class T>
class Blah;

template <class T>
class Blah< boost::tuple<T> >
{
  void operator()(T arg);
};

template <class T, class U>
class Blah< boost::tuple<T, U> >
{
  void operator()(T arg1, U arg2);
};

etc. etc.

john
  • 85,011
  • 4
  • 57
  • 81