C++ variadic template template parameter pack匹配失败问题咨询
问题根本原因
- C++对模板模板参数的签名匹配是严格的。你声明的
template<class...> class ... Templates要求传入的模板的所有参数都必须是类型参数,不允许包含非类型参数、模板模板参数。- 第一个示例中
Obj的模板参数包含int B非类型参数,签名和要求不符,匹配失败。 - 第二个示例中
Obj的模板参数包含template <class, class> class B模板模板参数,同样不符合“所有参数都是类型”的要求,因此仍然匹配失败。
- 第一个示例中
- 多个模板参数包有语法限制。C++标准要求模板参数列表中只能有一个末尾参数包,其余参数包必须能够通过实参推导或者有默认值,你写的
template< template<typename... > typename ... Templates, class ... Types >把模板模板参数包放在普通类型参数包前面,编译器无法分割两个包的实参范围,因此报错。
可行解决方案
方案1:匹配模板模板参数签名
如果明确知道要传入的模板的参数列表,直接把Foo的模板模板参数声明和目标模板的签名对齐即可,以第一个示例为例,修复后代码如下:
template< class A, int B, class C> struct Obj { A a_obj; static constexpr int b_value = B; C c_obj; }; // 模板模板参数签名和Obj对齐,允许接收第一个参数是类型、第二个是int、第三个是类型的模板 template< template<class, int, class> class ... Templates > struct Foo; template<> struct Foo<Obj> { Obj<void*, 5, float> obj; }; int main() { Foo<Obj> foo; return 0; }
上述代码在C++17标准下可以正常编译运行。
方案2:引入包装层兼容任意模板签名
如果需要Foo支持接收任意签名的模板,不需要每次修改Foo的模板参数声明,可以加一层模板包装器,把模板和它的参数打包成普通类型传入Foo:
// 通用模板包装器,兼容任意参数类型的模板 template<template<auto...> class Tmpl, auto... Args> struct TmplPack { using type = Tmpl<Args...>; }; // Foo仅接收普通类型参数包,无需处理模板模板参数的签名匹配 template<typename... TmplPacks> struct Foo; // 示例用法,包装Obj后传入 template<> struct Foo<TmplPack<Obj, void*, 5, float>> { typename TmplPack<Obj, void*, 5, float>::type obj; }; int main() { Foo<TmplPack<Obj, void*, 5, float>> foo; return 0; }
这种方案也支持包含模板模板参数的模板传入,只需要调整包装器的参数适配即可。
方案3:C++20泛型模板模板参数
如果你可以升级到C++20标准,可以用auto占位符简化任意模板的匹配:
// C++20及以上支持,auto占位符可以匹配任意类型、非类型、模板模板参数 template<template<auto...> class ... Templates> struct Foo;
这种写法可以直接兼容大部分参数签名的模板,无需手动对齐签名。
内容的提问来源于stack exchange,提问作者lurscher
相关产品推荐
相关产品推荐

