类非类型模板参数上的c ++成员函数模板专业化

时间:2020-11-12 15:19:40

标签: c++ templates template-specialization function-templates

从以下示例开始:

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

我现在想定义专门用于“ bar()”值的附加“ Y”功能。特别是类似下面插入的行(很抱歉,我不知道如何突出显示它们):

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

可悲的是,这些方法似乎都不是有效/可编译的(gcc / 9.3.0,-std = c ++ 11)。例如:

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

我知道我不能部分地专门化一个函数(这是我真正想做的),但我想在这里我只是部分地对struct进行了部分化,而完全对函数进行了专门化。

问题是,如何定义“ bar()”的其他规范作为成员函数?

(关于为什么说“ bar()”是模板计算,而“ Y”是模板的大小。基于“ Y”,我可能有不同的实现针对某些尺寸进行了优化。)

2 个答案:

答案 0 :(得分:2)

关于为什么,说“条”是模板计算,“ Y”是模板的大小。基于“ Y”,我可能有针对特定大小优化的不同实现。

所以我的建议是:避免专门化,并使用带有标签分派的重载。

一般情况下的模板bar(),特殊情况下的非模板bar()

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

答案 1 :(得分:1)

不允许直接做你想做的事。显式专业化要求您为所有模板参数指定显式值。这意味着类模板参数和成员函数模板参数。例如,这将是合法的:

template<>
template<>
void C<1,1>::bar<1>() { printf("Special %d/1\n",1); }

但是,如果您不使用template<>,那么您要么定义一个先前声明的成员,要么就处于部分专业化的领域,但是由于类模板和变量模板的原因,部分专业化才被允许。函数重载和部分专业化之间的冲突。

请注意,显式专业化不是模板(尽管使用template<>语法)。这就是为什么重要的是不要在类模板内出现它。

但是,可以通过推迟到一个可以部分专门化的单独的类模板来解决这些问题:

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

程序标准输出

Generic 0/0
Special 0/1
Special 0/2