为啥可变参数模板的模板特化与非可变模板的特化不同?

Posted

技术标签:

【中文标题】为啥可变参数模板的模板特化与非可变模板的特化不同?【英文标题】:Why is template specialization of variadic templates different from specialization of non-variadic templates?为什么可变参数模板的模板特化与非可变模板的特化不同? 【发布时间】:2014-08-18 14:34:36 【问题描述】:

我不明白为什么可变参数模板的模板专业化不同于常规(即非可变参数)模板。例如,我有一个模板和一个专业化,例如:

enum class MF : int 
    ZERO = 0,
    ONE = 1,
    TWO = 2
;

// --------- Specialization -------------
template <MF mf>
class Stat
public:
    Stat(std::string msg) 
        cout << "Generic Stat construtor: " << msg << endl;
    
;

// --------- Template Specialization -------------
template<>
class Stat<MF::ONE>
public:
    Stat(std::string msg) 
        cout << "Specialized Stat constructor: " << msg << endl;
    
;

我专门研究了MF 枚举的特定值。

现在,如果我想专门化一个可变参数模板,我不能用 MF 枚举的特定值(例如,MF::ONE)专门化可变参数模板参数,我只能专门化一个类型,(例如 @ 987654326@).

// --------- Variadic Template -------------
template<MF mf, typename... E>
class Var
public:
    Var(std::string msg)
        cout << "Generic Var constructor: " << msg << endl;
    

;

// --------- Variadic Template Specialization -------------
template<>
class Var<MF::TWO, MF>
public:
    Var(std::string msg)
        cout << "Specialized Var constructor: " << msg << endl;
    
;

我想将我的可变参数模板专门用于特定的MF 值,但似乎我做不到。

我是否缺少语言的某些方面可以让我做我想做的事?大致如下:

template<>
class Var<MF::TWO, MF::ONE>
public:
    Var(std::string msg)
        cout << "Specialized Var constructor: " << msg << endl;
    
;

完整的例子可以在here找到

【问题讨论】:

but it doesn't appear that I can. 你有没有收到错误或什么? MF::ONEtypename 吗?那么,您为什么期望能够将其传递给typename... MF::ONE 是一个枚举值。 @Paranaix 如果我像class Var&lt;MF::TWO, MF::ONE&gt; 那样专精,代码将无法编译。我收到此错误error: template argument for template type parameter must be a type class Var&lt;MF::TWO, MF::ONE&gt; 【参考方案1】:

如果我对您的理解正确,您希望您的可变参数模板使用任意数量的 MF 值进行参数化。好吧,不要这样声明:

template<MF mf, typename... E>

...它声明了任意数量的类型参数,但是像这样:

template<MF mf, MF... moreMF>

...声明任意数量的MF类型的非类型参数

也就是说,显式特化对于可变参数模板没有什么不同,但是您不知何故忘记了如何在两者之间设置参数。

【讨论】:

第一个参数必须是MF 类型的b,但其他模板参数不必是MF,而是可以是多种不同类型中的任何一种。我刚刚使用了MF,因为它是我已经拥有的。很抱歉造成混乱。【参考方案2】:

templates 不是宏。 typename... 并不意味着“采用任意数量的逗号分隔字符串”。 typename... 表示“取 0 种或更多类型”。

MF::ONE 不是类型。

尝试传递MF::ONE,而template 期望typename... 是错误的,因为template 要求的是typename 而不是值。

你可以写一个template class,它需要0个或多个MFs。你可以写一个接受零个或多个类型的。你不能写一个 template class 接受 0 个或多个 MFs 或类型。 C++ 不支持。

如果必须通过类型传递值,则有std::integral_constant&lt; TYPE, VALUE &gt;,它是一种包装整型常量值的类型。

template<MF mf, typename... E>
class Var;
template<>
class Var<MF::TWO, std::integral_constant<MF, MF::ONE>>
public:
  Var(std::string msg)
    cout << "Specialized Var constructor: " << msg << endl;
  
;

您可以通过 Var&lt;MF::TWO, std::integral_constant&lt;MF, MF::ONE&gt;&gt; 实例化它。

您可以创建别名:

template<MF mf> using MF_t=std::integral_constant<MF, mf>;

让你的代码看起来更漂亮:

template<MF mf, typename... E>
class Var;
template<>
class Var<MF::TWO, MF_t<MF::ONE>>
public:
  Var(std::string msg)
    cout << "Specialized Var constructor: " << msg << endl;
  
;
int main() 
  Var<MF::TWO, MF_t<MF::ONE>> instance("hello");;

出于元编程的目的,有时将模板编写为始终采用类型而不采用常量是一个明智的想法,并且当您需要常量时将其填充到上述类型中。然后template&lt;class...&gt;class X 将匹配任何这样的模板。

您可以使用::value 或通过构造它并将其转换为该类型的值来访问std::integral_constant 的值。 (在 C++1y 和许多 C++11 编译器中,该转换为 constexpr)。

所以

 MF x = MF_t<MF::ONE>;
 MF y = MF_t<MF::ONE>::value;

会将xy 设置为MF::ONE。在这两种情况下,右侧都应该是编译时表达式,所以:

MF_t< MF_t<MF::ONE> >

是与MF_t&lt; MF::ONE &gt; 相同的有效类型(如果写得有点傻)。

【讨论】:

感谢解释演示!这使它起作用。

以上是关于为啥可变参数模板的模板特化与非可变模板的特化不同?的主要内容,如果未能解决你的问题,请参考以下文章

C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译

为啥模板特化需要内联定义? [复制]

[ C++ ] template 模板进阶 (特化,分离编译)

C++模板进阶操作 —— 非类型模板参数模板的特化以及模板的分离编译

C++初阶:模板进阶非类型模板参数 | 模板的特化 | 模板分离编译

C++初阶:模板进阶非类型模板参数 | 模板的特化 | 模板分离编译