为啥这个嵌套的可变参数模板是无效参数?
Posted
技术标签:
【中文标题】为啥这个嵌套的可变参数模板是无效参数?【英文标题】:Why is this nested variadic template an invalid argument?为什么这个嵌套的可变参数模板是无效参数? 【发布时间】:2015-07-29 08:27:58 【问题描述】:如果我定义一个接受模板参数的struct
模板Bar
:
template <template <int,bool,char> class>
struct Bar ;
我可以使用struct
模板实例化它,例如Zod
:
template <int,bool,char> struct Zod ;
Bar<Zod> a;
我也可以使用嵌套的struct
模板来实例化它,例如JKL
:
struct GHI
template <int,bool,char>
struct JKL ;
;
Bar <GHI::JKL> b;
为什么我不能使用嵌套的可变参数struct
模板(例如DEF
)来实例化Bar
?:
template <typename ...Ts>
struct ABC
template <Ts ...>
struct DEF ;
;
Bar<ABC<int,bool,char>::DEF> c;
G++ 4.9.2 抱怨类型/值不匹配;而 Clang 3.4.2 的错误报告模板模板参数的模板参数与其对应的模板模板参数不同。
【问题讨论】:
在 gcc 5.1.0 或 clang 3.6.0 上也不能编译,因为它的价值。 @vsoftcoABC<int,bool,char>::DEF<4,true,'c'> foo;
工作。
棘手的部分是DEF
实际上采用了非类型模板参数包。请参阅 [temp.param]/p15 中的示例。
@Barry 我的意思是DEF
在技术上是通过非类型参数包参数化的。并且带有包的模板模板参数与不带包的模板模板参数不匹配。
@AndyProwl 这无关紧要。它仍然是一个参数包,这就是 §14.3.3/3 不允许匹配的原因。
【参考方案1】:
让我们给 DEF 的参数包起一个名字以便于参考:
template <typename ...Ts>
struct ABC
template <Ts ... Values>
struct DEF ;
;
这里的重点是,通过 [temp.param]/p15,Ts... Values
both 是 Ts
的包扩展和参数包 Values
的声明。 p>
如果一个 template-parameter 是 [...] 一个 parameter-declaration 声明一个参数包(8.3.5),那么 template-parameter 是一个 模板参数包 (14.5.3)。模板参数包是 parameter-declaration 其类型包含一个或多个未扩展的参数包是包扩展。
由于DEF
采用非类型参数包,它与不带包的模板模板参数不匹配([temp.arg.template]/p3):
一个 template-argument 匹配一个模板 template-parameter P 当 中的每个模板参数 template-argument对应的类模板或别名模板A的template-parameter-list匹配对应的模板 P的template-parameter-list中的参数。两个模板参数 如果它们属于同一类型(类型、非类型、模板),则匹配,对于 非类型模板参数,它们的类型是等价的(14.5.6.1), 而对于模板template-parameter,它们的每一个对应的 模板参数的匹配,递归。当 P 的 template-parameter-list 包含模板参数包(14.5.3)时,模板参数包会匹配零个或多个模板 中的参数或模板参数包 A的template-parameter-list与P中的模板参数包的类型和形式相同(忽略那些模板是否 参数是模板参数包)。
可以肯定的是,Values
对于包来说是相当奇怪的 - 对于 ABC
的每个特化,Values
必须包含固定数量的参数 - 但在当前规则下它仍然是一个包,所以包的规则申请。
【讨论】:
我想我理解你的解释,但我仍然有不清楚的地方。特别是,您指的是哪些“包装规则”?请注意,您引用的段落的最后一部分是指相反的情况(即,当 parameter 而不是参数是一个包时)。引用的第一部分提到如果相应的模板参数匹配,则模板模板参数匹配模板模板参数,并且它们可以是三种:“类型”、“非类型”和“模板”。我没有看到“packs”的特殊规则,“pack”也不是一种参数 @AndyProwl 对。后半部分表示如果参数中有一个包,它可以匹配参数中的包和非包。默认情况下,如果参数不是包,那么它不能匹配参数中的包,因为没有任何东西说参数中的(非包)模板参数可以匹配参数中的参数包。 使用template<template<int...> class> struct ;
和Bar<ABC<int,int,int>::DEF> c;
,它可以编译。但是他们都必须是int
s。因此,在匹配模板模板参数时,编译器似乎会区分模板参数包和 3 个模板参数...【参考方案2】:
就像巴里之前所说的:
ABC<int,bool,char>::DEF<4,true,'c'> foo
并尝试在此站点Compiler 上使用 Coliru 在线编译器 gcc 5.1 c++14:
#include <iostream>
template <template <int,bool,char> class>
struct Bar ;
template <int,bool,char> struct Zod ;
Bar<Zod> a;
struct GHI
template <int,bool,char>
struct JKL ;
;
Bar <GHI::JKL> b;
template <template <typename... Ts> class>
struct Base ;
template<typename... Ts>
struct Floor ;
Base<Floor> c;
template <typename... Ts>
struct ABC
template <Ts... val>
struct DEF ;
;
ABC<int,bool,char>::DEF<4,true,'c'> foo;
我搜索了一下,找到了这个模板参数列表。
包扩展可能出现在模板参数列表中:
template<typename... T> struct value_holder
template<T... Values> // expands to a non-type template parameter
struct apply ; // list, such as <int, char, int(&)[5]>
;
我在运行代码编译器中测试了一些东西: http://en.cppreference.com/w/cpp/language/parameter_pack 但我也在visual studio 2013中找到了这个Ellipses and Variadic Templates: https://msdn.microsoft.com/en-us/library/dn439779.aspx
【讨论】:
以上是关于为啥这个嵌套的可变参数模板是无效参数?的主要内容,如果未能解决你的问题,请参考以下文章