C++非类型模板函数重载特化匹配歧义问题咨询
问题原因分析
这是C++函数模板偏序(Partial Ordering of Function Templates)规则对类型参数的模式匹配和非类型参数的依赖常量表达式的处理差异导致的。
1. 为什么qwe的特化版本可以正常匹配
qwe的两个重载如下:
- 基础版:
template <class T,class F> void qwe(TypeFlag<T>,TypeFlag<F>) - 特化版:
template <class T> void qwe(TypeFlag<T>,TypeFlag<T*>)
函数模板重载决议时,会通过双向推导测试判断特化程度:如果模板A的所有合法实例都能匹配模板B,反之不成立,则A更特化,优先级更高。
- 从特化版推导基础版:给特化版代入任意类型
T=U,得到参数TypeFlag<U>, TypeFlag<U*>,可以直接推导出基础版的T=U, F=U*,推导成功,说明特化版的所有实例都能被基础版匹配。 - 从基础版推导特化版:给基础版代入任意两个独立类型
T=U, F=V,得到参数TypeFlag<U>, TypeFlag<V>,要匹配特化版需要满足V = U*,这不是普遍成立的,推导失败,说明基础版的实例不一定能匹配特化版。
双向测试结果明确特化版更特化,因此调用时会优先选择该版本。
2. 为什么asd的两个版本会触发歧义错误
asd的两个重载如下:
- 基础版:
template <size_t START, size_t END> void asd(SizeFlag<START>, SizeFlag<END>) - 候选特化版:
template <size_t START> void asd(SizeFlag<START>, SizeFlag<START + 1>)
非类型模板参数的偏序推导有一条关键规则:如果非类型模板参数的实参是依赖于其他模板参数的常量表达式(比如这里的START + 1),那么这个参数属于非推导语境,不会参与偏序时的模式匹配。C++标准明确要求模板推导不会对算术表达式进行反向求解,也就是说编译器不会通过START + 1 = M反推出START = M - 1。
对asd做双向测试时:
- 从候选特化版推导基础版:给候选特化版代入任意值
START=N,得到参数SizeFlag<N>, SizeFlag<N+1>,可以直接推导出基础版的START=N, END=N+1,推导成功。 - 从基础版推导候选特化版:给基础版代入任意两个独立值
START=N, END=M,得到参数SizeFlag<N>, SizeFlag<M>。第一个参数可以推导出候选特化版的START=N,但第二个参数要求是START + 1,属于依赖表达式不参与推导,编译器无法验证匹配性。因此偏序比较时只有第一个参数生效,两个模板的第一个参数完全一致,编译器判定二者特化程度相同,因此报歧义错误。
内容的提问来源于stack exchange,提问作者uncertainty principle
相关产品推荐
相关产品推荐

