C++重载函数赋值函数指针无歧义原因咨询
C++函数模板重载解析疑问:调用与指针赋值的歧义差异
#include <type_traits> template<typename Tp> struct depend_type { constexpr static bool false_ = false; }; template<typename Tp> struct cont { using x = void; static_assert(std::is_same_v<Tp, int>); // 取消注释后会触发编译错误 -> 'cont<Tp>::x' 会在 'void (*p)(int) = func;' 中被实例化 // static_assert(depend_type<Tp>::false_); }; template<typename Tp> void func(Tp) { } template<typename Tp> typename cont<Tp>::x func(Tp); int main(int /* argc */, char * /*argv*/[]) { // func(1); 会出现调用歧义 void (*p)(int) = func; // 为何此处无歧义? return 0; }
疑问
为何直接调用func(1)会出现歧义,但将func赋值给函数指针void (*p)(int)时却没有歧义?
核心原因分析
1. 直接调用func(1)时的歧义
当调用func(1)时,编译器会对两个函数模板执行实参推导:
- 第一个模板推导
Tp=int,生成签名为void func(int)的函数。 - 第二个模板同样推导
Tp=int,此时会实例化cont<int>(其中的static_assert(std::is_same_v<Tp, int>)条件成立),其成员类型x为void,因此也生成签名为void func(int)的函数。
重载决议阶段,这两个函数签名完全一致,且没有优先级差异(既非更特化的模板,也非非模板函数),编译器无法确定调用哪一个,因此抛出歧义错误。
2. 赋值给函数指针时无歧义的原因
当将func赋值给void (*p)(int)时,编译器需要将模板函数转换为匹配的函数指针类型:
- 两个模板都能推导得到
Tp=int,生成相同签名的void func(int)函数。 - 根据C++标准,这两个模板属于功能等价但不等价的模板(返回类型的定义形式不同,但实际类型一致),这种情况属于“无需诊断的病态程序”(ill-formed, no diagnostic required)——标准不强制编译器报错,但允许编译器自行处理。
- 实际编译器的行为是优先选择第一个出现的模板实例完成赋值,因此不会触发歧义错误。
额外说明:当你取消注释第二个static_assert(depend_type<Tp>::false_)时,实例化cont<Tp>会触发断言失败,此时第二个模板会因SFINAE规则被排除,但断言失败属于硬错误,直接导致编译失败。
内容的提问来源于stack exchange,提问作者Евгений Лисицын
相关产品推荐
相关产品推荐

