Reputation: 67224
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
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
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
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
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