简体   繁体   中英

c++ member function template specialization on class non-type template parameter

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.)

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();
 }

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM