Reputation: 147
Starting with the following example:
#include <cstdio>
template<int X, int Y>
struct C {
template<int Z> void bar() {printf("Generic %d/%d\n",X,Y);}
void foo() {bar<Y>();}
};
int
main(int argc, char *argv[])
{
C<0,0> c0;
c0.foo();
C<0,1> c1;
c1.foo();
}
I'd like to now define additional "bar()
" functions specialized on the value of "Y
". Specifically something like the inserted lines below (sorry I don't know how to otherwise highlight them):
#include <cstdio>
template<int X, int Y>
struct C {
template<int Z> void bar() {printf("Generic %d/%d\n",X,Z);}
template<> void bar<1>() {printf("Special %d/1\n",X);}
void foo() {bar<Y>();}
};
template<int X> template<> C<X,2>::bar<2>() {printf("Special %d/2\n",X);}
int
main(int argc, char *argv[])
{
C<0,0> c0;
c0.foo();
C<0,1> c1;
c1.foo();
}
Sadly, neither of those approaches seems to be valid/compile (gcc/9.3.0, -std=c++11). ex:
tempspec.cpp:94:12: error: explicit specialization in non-namespace scope ‘struct C<X, Y>’
94 | template<> void bar<1>() {printf("Special %d/1\n",X);}
| ^
tempspec.cpp:94:26: error: template-id ‘bar<1>’ in declaration of primary template
94 | template<> void bar<1>() {printf("Special %d/1\n",X);}
| ^
tempspec.cpp:97:33: error: expected initializer before ‘<’ token
97 | template<int X> void C<X,2>::bar<2>() {printf("Special %d/2\n",X);}
| ^
I know I can't partially specialize a function (which is what I really want to do) but I thought here I was partially specializing the struct, and fully specializing the function.
So the question is, how do I define additional specifications of "bar()
" as a member function?
(As for why, say that "bar()
" is a stencil computation and "Y
" is the size of the stencil. Based on "Y
" I may have different implementations optimized for certain sizes.)
Upvotes: 3
Views: 585
Reputation: 64298
Directly doing what you want isn't allowed. Explicit specialization requires that you specify explicit values for all template parameters. This means both the class template parameters and the member function template parameters. For example, this would be legal:
template<>
template<>
void C<1,1>::bar<1>() { printf("Special %d/1\n",1); }
But if you don't use template<>
, then you are either defining a previously declared member, or you are in the land of partial specialization, but partial specialization is only allowed for class templates and for variable templates due to conflicts between function overloading and partial specialization.
Note that an explicit specialization is not a template (despite the template<>
syntax). Which is why is it important that it not occur inside the class template.
However, these kinds of issues can be worked around by deferring to a separate class template that you can partially specialize:
#include <cstdio>
template<int X, int Y> struct C;
template <int X, int Y, int Z>
struct CBar {
static void bar(C<X,Y> &) {
printf("Generic %d/%d\n",X,Z);
}
};
template <int X, int Y>
struct CBar<X,Y,1> {
static void bar(C<X,Y> &) {
printf("Special %d/%d\n",X,1);
}
};
template <int X>
struct CBar<X,2,2> {
static void bar(C<X,2> &) {
printf("Special %d/%d\n",X,2);
}
};
template<int X, int Y>
struct C {
template<int Z> void bar() { CBar<X,Y,Z>::bar(*this); }
void foo() {bar<Y>();}
};
int main(int , char *[])
{
C<0,0> c0;
c0.foo();
C<0,1> c1;
c1.foo();
C<0,2> c2;
c2.foo();
}
Program stdout
Generic 0/0
Special 0/1
Special 0/2
Upvotes: 1
Reputation: 66190
As for why, say that "bar" is a stencil computation and "Y" is the size of the stencil. Based on "Y" I may have different implementations optimized for certain sizes.
So my suggestion is: avoid specialization and use overloading with tag-dispatching.
A template bar()
for generic case a some non-template bar()
for special cases
#include <iostream>
template <int X, int Y>
struct C
{
template <typename T>
void bar (T const &)
{ std::cout << "Generic " << X << '/' << Y << '\n'; }
void bar (std::integral_constant<int, 1> const &)
{ std::cout << "Special " << X << '/' << 1 << '\n'; }
void bar (std::integral_constant<int, 2> const &)
{ std::cout << "Special " << X << '/' << 2 << '\n'; }
void bar (std::integral_constant<int, 4> const &)
{ std::cout << "Special " << X << '/' << 4 << '\n'; }
void foo ()
{ bar(std::integral_constant<int, Y>{}); }
};
int main ()
{
C<0,0>{}.foo();
C<0,1>{}.foo();
C<0,2>{}.foo();
C<0,3>{}.foo();
C<0,4>{}.foo();
C<0,5>{}.foo();
}
Upvotes: 3