C++模板模板参数柯里化匹配问题及C++17编译异常咨询
问题解析:模板柯里化与C++模板模板参数匹配规则
这些现象不属于未定义行为,本质是C++17前后模板模板参数匹配规则的明确差异,以及GCC对标准规则的具体实现。以下是详细解释:
1. C++17之前的严格匹配规则
在C++17之前,模板模板参数的匹配要求参数数量完全一致,且非变长模板无法匹配变长模板:
Receive1要求的是接受1个固定类型参数的模板(template <typename> class)。Curry<...>::result是一个变长模板(template <typename... Args> using result = ...),在旧标准下,变长模板不能匹配非变长的模板模板参数,这就是直接写Receive1<Curry<Curry<Test, void>::result, void>::result>编译失败的原因。- 而
Alias是明确的单参数模板别名(template <typename Tp> using Alias = ...),参数数量固定为1,完全符合Receive1的要求,因此编译通过。
2. C++17的兼容匹配规则
C++17放宽了模板模板参数的匹配要求,引入兼容匹配规则:
当模板模板参数是非变长模板(如Receive1的单参数要求),而传入的实参是变长模板时,只要变长模板的参数包可以容纳模板模板参数要求的参数数量,就允许匹配。简单来说:变长模板可以匹配参数数量更少的非变长模板模板参数。
这就是添加-std=c++17后,Receive1<Curry<Curry<Test, void>::result, void>::result>能编译通过的原因——该result是变长模板,可接受1个参数(参数包Args...刚好容纳1个类型),满足Receive1的要求。
3. 看似“不符合预期”的编译通过原因
你提到的几个意外编译通过的案例,核心是模板模板参数的匹配仅检查模板的参数列表兼容性,不检查实例化后的合法性:
Receive1<Curry<Test, void>::result>{}:Curry<Test, void>::result是变长模板,可接受1个参数(参数包Args...取1个类型),符合Receive1的模板参数要求。虽然实例化Curry<Test, void>::result<T>会因Test需要3个参数而失败,但匹配阶段不做实例化检查,因此编译器允许通过。Receive2<Curry<Curry<Test, void>::result, void>::result>{}:该result是变长模板,可接受2个参数(参数包Args...取2个类型),满足Receive2的双参数模板要求,因此匹配通过。同理,实例化时会因参数数量超出Test的要求而失败,但匹配阶段不关心。Receive3<Curry<Curry<Test, void>::result, void>::result>{}:该result是变长模板,参数包可容纳3个类型,符合Receive3的三参数模板要求,因此匹配通过,即使实例化会因Test参数数量超限而失败。
这些行为完全符合C++17标准,并非未定义行为,只是匹配规则和你预期的“实例化合法性检查”不重合。
4. GCC版本的影响
你的GCC 8.3.0是C17标准的早期实现,对兼容匹配规则的支持符合标准。不同编译器或版本可能在细节上有差异,但核心规则是C17明确规定的。
内容的提问来源于stack exchange,提问作者youtao guo
相关产品推荐
相关产品推荐

