C++中仅模板参数不同的函数模板重载规则疑问
C++函数模板重载的规则说明
你对普通函数重载的认知是正确的:普通函数的重载判定不会将返回值类型作为区分依据,仅返回值不同的普通函数属于非法重定义。但这一规则不能直接套用到函数模板上,你写的两个input模板可以正常编译,核心依据是C++标准中函数模板签名构成规则与模板重载推导规则,具体如下:
两个模板的签名存在本质差异
函数模板的签名构成和普通函数不同,除了函数名、函数形参列表、所属命名空间/类这些普通函数也有的签名要素外,模板形参列表的长度、构成,以及依赖模板参数的返回值类型,都属于函数模板签名的合法组成部分,可以作为重载的区分依据。
你写的两个模板签名从模板参数层面就完全不同:
- 第一个模板是双类型形参的函数模板:
它需要两个模板类型参数,其中template <typename IN, typename ask> IN input(ask& str)IN仅出现在返回值位置,不会从函数实参推导。 - 第二个模板是单类型形参的函数模板:
它仅需要一个模板类型参数,返回值固定为template <typename ask> string input(ask& str)std::string,不依赖额外模板参数。
重载决议时不会产生歧义
两个模板之所以能正常共存,核心是所有合法调用场景下,编译器都能唯一确定匹配的版本,不会出现二义性:
- 当你不显式指定模板参数直接调用,比如
string s = input("请输入内容:"):第一个模板的IN参数没有出现在函数形参列表中,编译器无法从传入的实参推导IN的类型,根据SFINAE规则,这个版本会直接被从重载候选集中移除,只会匹配返回string的第二个模板。 - 当你显式指定第一个模板参数调用,比如
int num = input<int>("请输入数字:"):显式传入的int会匹配第一个模板的IN形参,第二个模板只有一个模板形参,传入的int会被当做ask的实参,推导出来的函数形参类型是int&,和你传入的字符串实参类型不匹配,第二个版本会被排除,只会匹配第一个模板。 - 如果你显式传入两个模板参数,比如
auto val = input<double, const char*>("请输入小数:"):第二个模板仅支持一个模板参数,传入两个实参时直接匹配失败,还是会选择第一个版本。
你可以用下面的代码验证调用逻辑,所有场景都不会出现编译错误:
#include <iostream> #include <string> using namespace std; template <typename IN, typename ask> IN input(ask& str){ IN i; cout << str ; cin >> i; return i; } template <typename ask> string input(ask& str){ string i; cout << str ; cin >> i; return i; } int main() { int age = input<int>("请输入年龄:"); string name = input("请输入姓名:"); double height = input<double>("请输入身高:"); cout << "姓名:" << name << "\n年龄:" << age << "\n身高:" << height << endl; return 0; }
补充说明
如果两个函数模板的模板参数列表长度、函数形参列表完全一致,仅返回值(且返回值不依赖模板参数)不同,依然属于非法重定义,比如下面的写法就无法通过编译:
// 错误:两个模板签名除了固定返回值外无差异,属于重定义 template <typename T> int func(T t) { return 1; } template <typename T> string func(T t) { return ""; }
内容的提问来源于stack exchange,提问作者PeterLiuAll
相关产品推荐
相关产品推荐

