4
votes

I'm learning C++11 variadic template and created a template struct to calculate the max number of a given list and tried:

#include <iostream>
#include <type_traits>

template <int a, int b, int... cs>
struct max: std::integral_constant<int, (a>b? max<a, cs...>::value: max<b, cs...>::value)> {};
template <int a, int b>
struct max<a, b>: std::integral_constant<int, (a>b? max<a>::value: max<b>::value)> {};
template <int a>
struct max<a>: std::integral_constant<int, a> {};

int main() {
  std::cout << max<2,1,5,7>::value << std::endl;
  return 0;
}

but g++ complains:

test.cc:7:58: error: wrong number of template arguments (1, should be at least 2)
 struct max<a, b>: std::integral_constant<int, (a>b? max<a>::value : max<b>::value)> {};

test.cc:9:13: error: wrong number of template arguments (1, should be at least 2)
 struct max<a>: std::integral_constant<int, a> {};

I can make it run by adding a simple declaration ahead:

template <int...>
struct max;

and change the first template above to:

template <int a, int b, int... cs>
struct max<a, b, cs...>: ...

I referred to cppreference: https://en.cppreference.com/w/cpp/language/partial_specialization#Partial_ordering but I cannot find any useful explanation for my case.

The problem may come from the last template (max<a>) with only one template parameter, which is not a specialized version of the primary one.

So my question is:

Why max<a> cannot be matched? Is there any rules or standards dealing with this?

=================================================================

okay, I found the C++ standards (Document No. N4659) that says:

[ Note: Partial specializations of class templates are found by looking up the primary class template and then considering all partial specializations of that template. If a using-declaration names a class template, partial specializations introduced after the using-declaration are effectively visible because the primary template is visible (17.5.5). — end note ]

So I think any partial specialized template that does not specialize from the base/primary one is considered to be an error, even though sometimes we can generate some non-specialized forms of expression from normal ones.

3

3 Answers

4
votes

When you define a class template as:

template <int a, int b, int... cs> struct max { ... };

The following is a valid specialization.

template <int a, int b> struct max<a, b> { ... };

However, the following is not.

template <int a> struct max<a> { ... };

since the base class template requires at least two template parameters.

2
votes

Not an answer, but why not simply...

template <int a, int... bs>
struct max : std::integral_constant<int,
   (a > max<bs...>::value ? a : max<bs...>::value)> {};

template <int a>
struct max<a> : std::integral_constant<int, a> {};

...?

2
votes

The reason you are getting that error is that every invocation of the template must first match the base template, and specialization only kicks in after that.

Your base template should match any number of arguments, and have no implementation (since max<>::value makes no sense), and have a single variadic argument, with all your other classes be specialization of that.

#include <iostream>
#include <type_traits>

template<int... cs>
struct max;

template <int a, int b, int... cs>
struct max<a, b, cs...>: std::integral_constant<int, (a>b? max<a, cs...>::value: max<b, cs...>::value)> {};
template <int a, int b>
struct max<a, b>: std::integral_constant<int, (a>b? max<a>::value: max<b>::value)> {};
template <int a>
struct max<a>: std::integral_constant<int, a> {};

int main() {
  std::cout << max<2,1,5,7>::value << std::endl;
  return 0;
}