函数签名不匹配的模板实例化:带默认参数的函数传参为何失败?
编译命令:
g++ -std=c++17 main.cpp -o main
待编译代码:
#include <iostream> template<typename TValue, typename UnaryPred> void foo(const TValue& t, UnaryPred pred) { pred(t); } void predWithTolerance(const double value, const double tol = 1e3) { std::cout << value << " " << tol << '\n'; } int main () { double a = 0; foo(a, predWithTolerance); }
编译报错信息:
main.cpp: In instantiation of ‘void foo(const TValue&, UnaryPred) [with TValue = double; UnaryPred = void (*)(double, double)]’: main.cpp:17:8: required from here main.cpp:6:9: error: too few arguments to function 6 | pred(t); | ~~~~^~~
已知可通过lambda表达式修复该问题,示例代码:
foo(a, [](double value) { return predWithTolerance(value); });
核心疑问:为何理论上可仅传一个实参调用的void (*)(double, double)类型函数指针,在此场景下无法正常调用?需结合C++标准相关规定解释根本原因。
根本原因解释
默认参数不属于函数类型的一部分
C++标准明确规定:函数的默认参数是函数声明的附属信息,不会被纳入函数的类型签名中。也就是说,predWithTolerance的函数类型是void(double, double),对应的函数指针类型为void (*)(double, double)——这个指针类型完全不包含任何默认参数的信息。当你把函数名predWithTolerance传递给模板foo时,它会退化为上述类型的函数指针,此时默认参数的信息已经丢失。函数指针调用不支持默认参数补全
在模板foo实例化后,pred的类型被推导为void (*)(double, double)。此时执行pred(t)时,编译器是在调用一个函数指针,而非直接调用原始函数。根据C++标准,函数指针的调用必须严格匹配其类型要求的参数数量和类型——该指针要求传入两个double参数,但这里只传了一个,因此触发"参数不足"的编译错误。默认参数仅在直接调用函数名的语法场景下生效,无法作用于函数指针的调用。lambda修复的原理
lambda表达式[](double value) { return predWithTolerance(value); }是一个自定义的一元函数对象,它的调用运算符仅接受一个参数。在lambda内部调用predWithTolerance(value)时,这是对原始函数的直接调用,此时编译器会识别到该函数声明中的默认参数,自动补全第二个参数1e3,因此能正常通过编译。
内容的提问来源于stack exchange,提问作者LRDPRDX

