C++取函数模板地址时最特化模板判定与编译器歧义问题
你遇到的情况属于GCC和Clang的已知实现缺陷,两个编译器的行为不符合当前C++标准的规定,你的标准规则推导逻辑是完全正确的。
#include <iostream> template<class T> void fun(T&){ //#1 std::cout<<"selected\n"; } template<class T> void fun(T&&){} //#2 int main() { void(*ptr)(int&) = &fun; //#3 }
标准规定的推导过程
候选集生成:根据
[temp.deduct.funcaddr#1]规则,两个函数模板都可以通过实参推导得到匹配目标类型void(int&)的特化:- 对#1:推导T为int,实例化得到
void fun(int&),完全匹配目标类型 - 对#2:推导T为int&,触发引用折叠后得到
void fun(int&),也完全匹配目标类型
因此两个模板都进入候选集。
- 对#1:推导T为int,实例化得到
偏序筛选:按照
[over.over#5]规则,需要通过函数模板偏序判断哪个候选更特化:
若候选项集合中存在第二个函数模板特化,其对应的函数模板按照[temp.func.order]的偏序规则比特化F1对应的模板更特化,则淘汰F1。
确定偏序所用的类型取决于偏序执行的上下文:在其他上下文中,使用函数模板自身的函数类型。
若P和A是取函数模板地址或从函数声明推导模板实参场景下产生的函数类型,且Pi、Ai分别为P和A顶层参数列表的参数,若Pi是转发引用、Ai是左值引用,则Pi会被调整,类型改为对应的模板参数类型(即T&&改为仅T)。
双向推导的结果:
- 从#1的函数类型推导#2的模板参数:#1的参数类型为
T&作为A,#2的参数T&&作为P,符合上述调整规则,P被调整为T,可成功推导#2的模板参数为T&,推导成立。 - 从#2的函数类型推导#1的模板参数:#2的参数类型为
T&&作为A,#1的参数T&作为P,没有适用的调整规则,无法从右值引用类型推导左值引用的模板参数,推导失败。
因此#1的模板更特化,应该淘汰#2的候选,最终仅保留#1的实例,不存在歧义。
编译器行为异常的原因
GCC和Clang在实现取函数模板地址的偏序逻辑时,错误地没有应用[temp.deduct.type#10]的参数类型调整规则,导致双向推导均失败,判定两个候选优先级相同,因此抛出歧义错误。该缺陷已经被两个编译器团队确认,但截至目前最新稳定版本仍未修复。
内容的提问来源于stack exchange,提问作者xmh0511
相关产品推荐
相关产品推荐

