为何MSVC 19中printf作为模板函数参数可通过,cos却编译失败?
编译报错原因解析
核心差异:函数是否存在重载
你给出的第一段代码中两行调用的编译结果差异,本质是printf和cos两个函数的签名特性不同:
- 标记为#2的
test(printf, "%s", "aaa")可以正常编译:printf是C标准库导出的函数,C++标准中从<cstdio>引入的printf没有任何重载,签名固定为int printf(const char*, ...)。当你把printf作为实参传给模板参数F时,编译器可以直接确定它的类型是对应函数指针,模板推导可以正常完成。 - 标记为#1的
test(cos, 0.1)编译失败:<cmath>中的cos是C++标准提供的重载函数,至少存在三个合法版本:
你直接传递float cos(float); double cos(double); long double cos(long double);cos作为实参时,没有额外上下文提示编译器选择哪个重载版本,模板参数F无法完成推导,因此编译报错。
如果你要让这行编译通过,可以显式指定重载类型,比如:test(static_cast<double(*)(double)>(cos), 0.1);
不同编译器表现差异的原因
你给出的第二个代码片段(注释掉<iostream>引入)出现MSVC可编译、GCC编译失败的情况,是两个编译器的标准库实现差异导致的:
- MSVC的标准库实现中,
<iostream>默认会引入<cmath>头,且C++编译模式下包含C头<math.h>也会自动注入cos的重载版本,因此可以正常推导。 - GCC的标准库实现中,C头
<math.h>默认把cos定义为宏,且没有自动引入C++的重载版本,宏展开后无法被识别为合法的函数指针对象,因此编译失败。你可以用括号包裹cos阻止宏展开来临时解决:test((cos), 0.1)。
C++20的相关规则变更
自C++20起,标准新增了可寻址函数的相关规定:除非是标准明确标记为可寻址的函数,否则取标准库函数地址的行为属于未定义行为,编译器有权直接拒绝这类代码的编译。
内容的提问来源于stack exchange,提问作者slow_down_the_pace
相关产品推荐
相关产品推荐

