【问题标题】:c++ member function template specialization on class non-type template parameterc++成员函数模板特化类非类型模板参数
【发布时间】:2020-11-12 15:19:40
【问题描述】:

从下面的例子开始:

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

我知道我不能部分特化一个函数(这是我真正想做的),但我认为我在这里部分特化了结构,并完全特化了函数。

那么问题来了,如何将“bar()”的附加规范定义为成员函数?

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

【问题讨论】:

  • 我认为您对模板有误解。模板参数的特化并不意味着使用一个值,而是一个类型。模板允许您为不同类型定义函数/类。它们更有可能是构建用户定义类型或函数的秘诀。 cplusplus.com/doc/oldtutorial/templates
  • @LucaJungla 可以提供值作为模板参数。它仅限于特定的简单类型。 en.cppreference.com/w/cpp/language/template_parameters
  • @justapony 您能否提供更多详细信息(更多要求)。你想达到什么目的?也许有更好的解决方案来解决您的问题。
  • 没有太多额外的细节。我的“模板”示例一般说明了用例:成员函数的专用/优化版本。这似乎是模板的自然使用,我想了解我在语法上做错了什么和/或了解为什么从 C++ 语言的角度来看,以这种方式使用模板是不合理的。我对让它工作的黑客不太感兴趣(毕竟我可以把“bar”写成一个大的case语句,#ifdef,编译时间if,等等......;^));更有兴趣了解为什么这不是“正确”的方法。
  • @justapony:我已经扩展了我的答案,试图进一步了解“为什么”。让我知道更多细节是否会有所帮助。

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


【解决方案1】:

至于为什么,说“bar”是模板计算,“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();
 }

【讨论】:

    【解决方案2】:

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

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

    但是如果你不使用template&lt;&gt;,那么你要么定义了一个先前声明的成员,要么你处于偏特化的领域,但是由于冲突,部分特化只允许用于类模板和变量模板在函数重载和部分特化之间。

    请注意,显式特化不是模板(尽管有template&lt;&gt; 语法)。这就是为什么它不出现在类模板中很重要。

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

    #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
    

    【讨论】:

    • 您已更改要解决的问题。他在模板类中有模板方法,您将问题转换为类模板。他还需要部分专业化类的方法的完全专业化。这是类似的问题stackoverflow.com/a/4995094/1387438 已解决。
    • @MarekR:我不明白你在说什么。我正在使用另一个类模板解决问题,但我看不出它是如何改变问题的。
    • @MarekR:我确实看到我专业化了错误的事情。我会解决的。
    • @MarekR:实际上,OP 的意图似乎是专门化 foo,而推迟到 bar 是一种尝试。
    猜你喜欢
    • 1970-01-01
    • 2013-12-20
    • 1970-01-01
    • 2013-09-24
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多