Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Partial Specialization in C++11

I have the following code:

template<class T, int I=44> struct test {T j = I;};

template<int J> struct test<int, J> {int j = J;};

int main()
{
  test<55> jj;

  std::cout << jj.j << std::endl;
  return(1);
}

The compiler (clang) complains only about the line test<55> jj

I don't understand why? Is there a work around?

And if it complains about that line, why doesn't it complain about the second template definition?

Thanks in advance.

The message is:

enable.cpp:17:8: error: template argument for template type parameter must be a type
test<55> jj;
   ^~
enable.cpp:9:16: note: template parameter is declared here
template<class T, int I=44> struct test
like image 760
Fred Finkle Avatar asked Jul 23 '12 15:07

Fred Finkle


2 Answers

The problem is that you haven't understood how selecting a class template specialization works.

Your specialization:

template<int J> struct test<int, J> {int j = J;};

does not create a template for which you only have to pass in a single int template parameter.

test<55> jj; // doesn't work because there's no template<int J> struct test

Instead what it does is create a specialization of template<class T, int I> struct test which will be used when the template arguments to template<class T, int I> struct test match the specialization, i.e. test<int,J>.

test<int,55> jj; // uses the specialization template<int J> struct test<int, J>

Here's the key quote from the standard:

In a type name that refers to a class template specialization, (e.g., A<int, int, 1>) the argument list shall match the template parameter list of the primary template. The template arguments of a specialization are deduced from the arguments of the primary template. [emphasis added]

                                                                                   — 14.5.5.1 [temp.class.spec.match] p4


You seem to be attempting to set int as a default type for T while simultaneously setting an independent default value for I. I think your intent is to be able to specify a type and a value, specify only a type and get 44 as a default value, or specify only a value and get int as a default type.

Unfortunately I don't know of a way to specify independent defaults like that. You can specify defaults (template<class T=int, int I=44> struct test) but getting the default type will also require accepting the default value.

However if you're willing to use a second name then you can do:

template <int I>
using test_int = test<int, I>;

This creates a template alias such that you only have to specify a value:

test_int<55> jj;

And this will end up using whatever specialization test<int, I> happens to resolve to whether there's an explicit specialization or the compiler generates an implicit one.

like image 200
bames53 Avatar answered Sep 18 '22 20:09

bames53


The first error occurs because the compiler tries to instantiate the first template parameter (class T) with 55. This does not work, because 55 is not a type itself, but an instantiation of it. However, test<int> works because here the template parater is a type, as requested by your template signature.

What you want instead is "currying" for types, aka. template aliasing:

template <typename T>
struct test2 = using test<T, 55>;

Here you only need to provide a T to choose 55 as fixed second parameter. But you still need to use that type by giving it a concrete T, e.g., test2<double>.

The comments indicated you could also be interested in the following variant:

template <int I>
using test3 = test<int, I>;

Here you fix the first type parameter to int, which allows you to use it as you did in your code:

test3<55> t;
like image 43
mavam Avatar answered Sep 17 '22 20:09

mavam