外部结构体可变参数类型在成员模板模板参数中的展开问题
C++可变参数模板模板参数匹配异常分析与修复
几种可行的C++模板用法
- 单模板参数结构体的成员模板可使用外部类型作为值参数:
template <typename T> struct SMoo { template <T I, typename T2> struct SBaa {}; }; SMoo<int>::SBaa<3, bool> moobaa;
- 可变参数外部模板的成员模板可展开参数作为值参数:
template <typename ... VT> struct SMoo { template <VT ... VI, typename T2> struct SBaa {}; }; SMoo<int, bool>::SBaa<3, true, bool> moobaa;
- 单模板参数结构体的成员模板可将外部类型作为模板模板参数的值参数:
template <typename T> struct SMoo { template <template <T I, typename T2> typename TT> struct SBaa {}; }; template <int I, typename T> struct SCaw {}; SMoo<int>::SBaa<SCaw> moobaacaw;
- 组合可变参数外部模板与模板模板参数的成员模板,单可变参数时可行:
template <typename ... VT> struct SMoo { template <template <VT ... VI, typename T2> typename TT> struct SBaa {}; }; template <int I, typename T> struct SCaw {}; SMoo<int>::SBaa<SCaw> moobaacaw;
出现的匹配异常
当外部可变参数为多个(如int, bool)时,匹配参数数量对应的模板SMew失败,但匹配参数更少的模板SCaw却能通过编译,报错信息如下:
// 补充SMew的定义 template <int I, bool B, typename T> struct SMew {}; SMoo<int, bool>::SBaa<SMew> moobaamew; // <<=== 此处报错
错误输出:
<FILE>:5:27: error: type/value mismatch at argument 1 in template parameter list for ‘template<class ... VT> template<template<template<VT ...VI, class T2> class TT> template<class ... VT> template<VT ...VI, class T2> class TT> struct SMoo<VT>::SBaa’ 15 | SMoo<int, bool>::SBaa<SMew> moobaamew; // <<=== FAILS HERE (WHY?) | ^ <FILE>:15:27: note: expected a template of type ‘template<class ... VT> template<VT ...VI, class T2> class TT’, got ‘template<int I, bool B, class VT> struct SMew’
异常原因
核心问题是模板模板参数中的参数名重复:
当SMoo被实例化为SMoo<int, bool>时,成员模板SBaa的模板模板参数template <VT ... VI, typename T2>会被展开为template <int VI, bool VI, typename T2>——两个非类型参数使用了相同的名称VI,违反了C++中模板参数必须唯一命名的规则。
这个错误在模板定义阶段不会触发(因为模板是延迟实例化的),只有在尝试匹配SMew时,编译器才会解析这个无效的声明,导致匹配失败。而SCaw能“成功”匹配是编译器的宽松处理:它只匹配了VT...的第一个类型和后面的类型参数,忽略了第二个类型,这并非标准允许的合法行为。
修复方案
只需要去掉模板模板参数中非类型参数的名称,避免展开后出现重名问题即可:
template <typename ... VT> struct SMoo { // 去掉参数名VI,直接写VT...,避免重名冲突 template <template <VT..., typename T2> typename TT> struct SBaa {}; }; template <int I, bool B, typename T> struct SMew {}; // 现在可以正常匹配SMew SMoo<int, bool>::SBaa<SMew> moobaamew;
如果需要兼容参数数量更少的模板(如SCaw),可以将类型参数包放在最后,允许匹配任意数量的后续类型参数:
template <typename ... VT> struct SMoo { template <template <VT..., typename...> typename TT> struct SBaa {}; }; template <int I, typename T> struct SCaw {}; // 此时SMew和SCaw都能正常匹配 SMoo<int, bool>::SBaa<SMew> moobaamew; SMoo<int, bool>::SBaa<SCaw> moobaacaw;
内容的提问来源于stack exchange,提问作者xaxazak
相关产品推荐
相关产品推荐

