bobobobo
bobobobo

Reputation: 67224

What is this template "specialization" code doing, and why does it compile but not work as expected?

I'm trying to specialize just one method of a class template.

The accepted answer there definitely does work, but what does this code mean to the compiler and why doesn't it work as expected?

#include <stdio.h>

template <typename T>
struct Node
{
  void split() { puts( "Default method" ) ; }

  // this compiles, but it doesn't appear to do anything!
  template <int> void split() { puts( "Int method" ) ; }
} ;

// This definitely works
//template <> void Node<int>::split() { puts( "Int method" ) ; }

int main()
{
  Node<double> n ;
  n.split() ; // "Default method"

  Node<int> i;
  i.split();  // "Default method" again!
}

Upvotes: 0

Views: 151

Answers (4)

bames53
bames53

Reputation: 88155

Let's look at the syntax for template specialization. First, here's a template

template<typename T> void bar() { std::cout << "generic\n"; }

Now, to declare a specialization, you have to provide a different definition for this template, with the template parameters 'filled in' for the template being specialized:

void bar<int>() { std::cout << "special for int\n"; }

except the above isn't legal because a template specialization must also be a template, with it's own parameters that are not the same as those of the template being specialized. In this case we don't have any other parameters so we have to just use an empty template parameter list:

template<>
void bar<int>() { std::cout << "special for int\n"; }

Note that when 'filling in' the template parameters the specific types go in the same place as they do when you use the template, not inside the template<...>

Here's the code from that other question on how to specialize one member of a class template for a specific intantiation of the template.

template <typename T>
struct Node
{
  void split() { puts( "Default method" ) ; }
};

template<>
void Node<int>::split() { puts( "Int method" ) ; }

This is the same as

template <typename T>
struct Node
{
  void split();
};

// A
template<typename T>
void Node<T>::split() { puts( "Default method" ) ; }

// B
template<>
void Node<int>::split() { puts( "Int method" ) ; }

As you can see, B is a specialization of A because the Node<int> in B fills in the parameters for A.

Now look at your code:

template <typename T>
struct Node
{
  void split() { puts( "Default method" ) ; }

  template <int> void split() { puts( "Int method" ) ; }
};

There's no specialization here. Nothing is 'filling in' template parameters for some other template.

So what does template<int> mean? At first glance you might think that it means you're 'filling in' some template<typename T> with T = int, but that is not the case. When you fill in template parameters for specializtion, you don't do it inside the template<...>.

Instead, template<int> is using something called "non-type template parameters". In template<typename T> you're creating a template that can be instantiated for different types. E.g.:

template <typename T> void bar();
bar<Foo>();
bar<std::string>();

template<int> is the same thing, except it lets you get different instantiations for different int values, rather than different types. See:

template <int N> void baz();
baz<1>();
baz<INT_MAX>();

In the one case the template parameter is a typename and so the template is instantiated with types. In the other case the template parameter is int and so the template is instantiated with int values.

Here's an example of a template that uses non-type template parameters:

template<int N>
struct array{
    double arr[N];
};

array<3> k = {{ 0.0, 0.0, 1.0 }}; // has a member 'double arr[3]'
array<2> j = {{ 0.0, 1.0 }}; // has a member 'double arr[2]'

And here's another where the value N is deduced:

template<typename T,int N>
int size_of_array(T (&my_array)[N]) { // N is deduced from the argument you pass size_of_array
    return N;
}

double X[100];
std::cout << size_of_array(X) << '\n'; // instantiates size_of_array<double,100>

Upvotes: 1

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361262

Call like this:

n.split<10>();
i.split<10>(); //or use any constant integral value

If you intend to call the function template. Note that the function template is not a specialization. It rather is a primary function template, and to call it, you've to provide the template argument explicitly.

You can do even this:

const int x = 100;
i.split<x>(); //ok - x is constant expression

But this is not allowed:

int y = 100;
i.split<y>(); //error - y is not constant expression

Upvotes: 3

Georg Fritzsche
Georg Fritzsche

Reputation: 98974

Your variation defines a seperate template function split that takes an integral (value) template parameter, not a specialization.

To call it you'd have to use e.g.:

n.split<1>();

Upvotes: 4

Mark Ransom
Mark Ransom

Reputation: 308111

Your class now has two different split methods in it, one templated and one not. If you want to see the output of the template method you have to call it explicitly.

i.split<1>();

Go back to the question you linked, that's the proper way to do it.

Upvotes: 0

Related Questions