为啥可变参数模板的模板特化与非可变模板的特化不同?
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::ONE
是 typename
吗?那么,您为什么期望能够将其传递给typename...
?
MF::ONE
是一个枚举值。
@Paranaix 如果我像class Var<MF::TWO, MF::ONE>
那样专精,代码将无法编译。我收到此错误error: template argument for template type parameter must be a type class Var<MF::TWO, MF::ONE>
【参考方案1】:
如果我对您的理解正确,您希望您的可变参数模板使用任意数量的 MF
值进行参数化。好吧,不要这样声明:
template<MF mf, typename... E>
...它声明了任意数量的类型参数,但是像这样:
template<MF mf, MF... moreMF>
...声明任意数量的MF
类型的非类型参数。
也就是说,显式特化对于可变参数模板没有什么不同,但是您不知何故忘记了如何在两者之间设置参数。
【讨论】:
第一个参数必须是MF
类型的b,但其他模板参数不必是MF
,而是可以是多种不同类型中的任何一种。我刚刚使用了MF
,因为它是我已经拥有的。很抱歉造成混乱。【参考方案2】:
template
s 不是宏。 typename...
并不意味着“采用任意数量的逗号分隔字符串”。 typename...
表示“取 0 种或更多类型”。
MF::ONE
不是类型。
尝试传递MF::ONE
,而template
期望typename...
是错误的,因为template
要求的是typename
而不是值。
你可以写一个template class
,它需要0个或多个MF
s。你可以写一个接受零个或多个类型的。你不能写一个 template class
接受 0 个或多个 MF
s 或类型。 C++ 不支持。
如果必须通过类型传递值,则有std::integral_constant< TYPE, VALUE >
,它是一种包装整型常量值的类型。
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<MF::TWO, std::integral_constant<MF, MF::ONE>>
实例化它。
您可以创建别名:
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<class...>class X
将匹配任何这样的模板。
您可以使用::value
或通过构造它并将其转换为该类型的值来访问std::integral_constant
的值。 (在 C++1y 和许多 C++11 编译器中,该转换为 constexpr
)。
所以
MF x = MF_t<MF::ONE>;
MF y = MF_t<MF::ONE>::value;
会将x
和y
设置为MF::ONE
。在这两种情况下,右侧都应该是编译时表达式,所以:
MF_t< MF_t<MF::ONE> >
是与MF_t< MF::ONE >
相同的有效类型(如果写得有点傻)。
【讨论】:
感谢解释和演示!这使它起作用。以上是关于为啥可变参数模板的模板特化与非可变模板的特化不同?的主要内容,如果未能解决你的问题,请参考以下文章
C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译
[ C++ ] template 模板进阶 (特化,分离编译)
C++模板进阶操作 —— 非类型模板参数模板的特化以及模板的分离编译