17

I have a variadic template function which calls itself to determine the largest number in a list (constituted by the templatized arguments). I am trying to make a specialization for when the parameter pack is empty so I can just return the number at the front of the list, but I don't know how to do that. I am just becoming familiar with variadic templates and template specialization, but this is what I have so far:

#include <string>
#include <iostream>

using namespace std;

template <int N, int... N2>
int tmax() {
    return N > tmax<N2...>() ? N : tmax<N2...>();
}

template <int N>
int tmax() {
    return N;
}

int main() {
    cout << tmax<32, 43, 54, 12, 23, 34>();
}

However, this produces the following error:

test.cpp: In function ‘int tmax() [with int N = 34, int ...N2 = {}]’:
test.cpp:9:45:   instantiated from ‘int tmax() [with int N = 23, int ...N2 = {34}]’
test.cpp:9:45:   instantiated from ‘int tmax() [with int N = 12, int ...N2 = {23, 34}]’
test.cpp:9:45:   instantiated from ‘int tmax() [with int N = 54, int ...N2 = {12, 23, 34}]’
test.cpp:9:45:   instantiated from ‘int tmax() [with int N = 43, int ...N2 = {54, 12, 23, 34}]’
test.cpp:9:45:   instantiated from ‘int tmax() [with int N = 32, int ...N2 = {43, 54, 12, 23, 34}]’
test.cpp:18:39:   instantiated from here
test.cpp:9:45: error: no matching function for call to ‘tmax()’
test.cpp:9:45: error: no matching function for call to ‘tmax()’

I have also tried this, just to see if it would work (although it introduces the number 0 to the list randomly so that it can't ever return a number less than 0):

template <int N, int... N2>
int tmax() {
    return N > tmax<N2...>() ? N : tmax<N2...>();
}

template <>
int tmax<>() {
    return 0;
}

However, in addition to the errors mentioned above, I get this error:

error: template-id ‘tmax<>’ for ‘int tmax()’ does not match any template declaration

So what should I do to get this working?

I am using g++ 4.5.2 with the -std=c++0x flag.

Xeo
  • 129,499
  • 52
  • 291
  • 397
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249

3 Answers3

29

I see two mistakes using clang.

  1. Put the overload taking a single int first.

  2. Make things unambiguous for lists of length 1. Recall that variadic lists can have zero size and when they do, it seems to me you have an ambiguity.

This compiles and runs correctly for me:

#include <iostream>

using namespace std;

template <int N>
int tmax() {
    return N;
}

template <int N, int N1, int... N2>
int tmax() {
    return N > tmax<N1, N2...>() ? N : tmax<N1, N2...>();
}

int main() {
    cout << tmax<32, 43, 54, 12, 23, 34>();
}

54

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    The need to explicitly "split" parameters pack in a head/tail fashion has bitten me quite a few times already, I expect we'll get used to it... however I don't think putting the base case first is mandatory, as it is, I usually put them immediately afterwards. – Matthieu M. Jun 22 '11 at 06:30
5

Personally, I'd prefer using static class members over functions for this sort of thing:

template <int... N> struct max;
template <int N, int... M> struct max<N, M...> {
  static const int value = max<N, max<M...>::value>::value;
};    
template <int N, int M> struct max<N, M> {
  static const int value = N > M ? N : M;
};

int main()
{
  return max<1,2,3>::value;
}

Update: Using ildjarn's suggestion, here's the less verbose version:

#include <type_traits>
template <int... N> struct max;
template <int N, int... M> struct max<N, M...>
  : std::integral_constant<int, max<N, max<M...>::value>::value> { };
template <int N, int M> struct max<N, M>
  : std::integral_constant<int, (N > M ? N : M)> { };
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

Since you can't partially specialize functions, you need to wrap your functionality:

template<int Head, int... Tail>
struct Tmax{
  static int do(){
    return Head > Tmax<Tail...>::do() ? Head : Tmax<Tail...>::do();
  }
};

template<int N>
struct Tmax<N>{
  static int do(){
    return N;
  }
};

template<int... Numbers>
int tmax(){
  return Tmax<Numbers...>::do();
}
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • not sure if it's legal, but g++-4.5.1 seems to not like your answer: http://ideone.com/VXqHv Don't have my copy of the standard on hand, so not sure if it's a real issue or an old compiler. – Lambdageek Jun 22 '11 at 01:23