函数模板调用规则

内容简介

函数模板不是简单的“万能函数”。当普通函数、函数模板、函数模板重载同时存在时,编译器会按匹配程度选择调用哪个函数。

核心规则

  • 普通函数和函数模板都能匹配时,通常优先调用普通函数。
  • 可以通过空模板参数列表 <> 强制调用模板。
  • 函数模板也可以重载。
  • 如果函数模板匹配更精确,可能优先调用函数模板。
  • 自动类型推导时,模板参数必须能推导出一致类型。

用法

普通函数允许隐式类型转换:

int add(int a, int b)
{
    return a + b;
}
 
int a = 10;
char c = 'c';
std::cout << add(a, c) << '\n';  // c 转成 int

函数模板自动推导时不做这种统一转换:

template <typename T>
T add_template(T a, T b)
{
    return a + b;
}
 
int a = 10;
char c = 'c';
 
// add_template(a, c);       // 错误:T 无法同时是 int 和 char
add_template<int>(a, c);     // 正确:显式指定 T 为 int
add_template<char>(a, c);    // 正确:显式指定 T 为 char

普通函数和模板函数的选择:

void print(int a, int b)
{
    std::cout << "normal\n";
}
 
template <typename T>
void print(T a, T b)
{
    std::cout << "template\n";
}
 
int x = 1;
int y = 2;
print(x, y);    // normal
print<>(x, y);  // template,强制调用模板
 
char m = 'a';
char n = 'b';
print(m, n);    // template,因为模板匹配更精确

最佳代码实践

  • 简单场景直接让编译器自动推导。
  • 自动推导失败或语义需要统一类型时,显式指定模板参数。
  • 模板和普通函数同时存在时,要有明确目的,避免让读者猜调用结果。
  • 复杂重载场景尽量用更清楚的函数名或 concepts 限制类型。

错误用法

template <typename T>
void process(T value);
 
void process(int value);
 
process(1);  // 初学者容易以为一定调用模板,实际普通函数更优先

注意事项