【cpluplus教程翻译】重载和模板(Overloads and templates)

重载函数

C++允许两个函数同名,只要参数不同,数目或类型不同都行,就不会编译报重定义的错误

// overloading functions
#include <iostream>
using namespace std;

int operate (int a, int b)
{
  return (a*b);
}

double operate (double a, double b)
{
  return (a/b);
}

int main ()
{
  int x=5,y=2;
  double n=5.0,m=2.0;
  cout << operate (x,y) << '\n';
  cout << operate (n,m) << '\n';
  return 0;
}

上面这个例子,两个函数都是operator,一个参数是int,一个是double,编译器通过参数类型可以明白应该调用哪个
这个例子两个函数的行为不一样,一个是乘法,一个是除法,这不是一个好的设计,不过也是完全符合语法规则的
注意只有返回值不同是不能被重载的(本质上函数返回都是放在eax寄存器里?

函数模板

重载函数的定义可能是完全一样的

// overloaded functions
#include <iostream>
using namespace std;

int sum (int a, int b)
{
  return a+b;
}

double sum (double a, double b)
{
  return a+b;
}

int main ()
{
  cout << sum (10,20) << '\n';
  cout << sum (1.0,1.5) << '\n';
  return 0;
}

sum虽然被重载了,但是函数体是一样的
sum可能会被很多类型重载,但是函数体都是一样的,因此可以使用函数模板减少代码,语法和函数一致,前面加template关键字,然后用尖括号包括模板参数即可

template <class SomeType>
SomeType sum (SomeType a, SomeType b)
{
  return a+b;
}

注意尖括号,和函数的参数列表一样,前面是类型,后面是参数名,用class和typename完全是一样的
上面的例子声明SomeType是一个通用类型,这样就可以在函数体里随便使用,可以是参数类型,也可以是返回值,可以用来声明其他变量,只是表示一个通用的类型
实例化模板是指用一个特定的类型替换模板参数来创造函数,name <template-arguments> (function-arguments),例子```c++
x = sum(10,20);

int只是一种实例化,实际上就和定义了一个函数差不多
```c++
int sum (int a, int b)
{
  return a+b;
}
// function template
#include <iostream>
using namespace std;

template <class T>
T sum (T a, T b)
{
  T result;
  result = a + b;
  return result;
}

int main () {
  int i=5, j=6, k;
  double f=2.0, g=0.5, h;
  k=sum<int>(i,j);
  h=sum<double>(f,g);
  cout << k << '\n';
  cout << h << '\n';
  return 0;
}

上面这个例子,我们用了两个模板函数,第一个模板参数是int,第二个是double。
编译器也可以通过类型推导选择需要实例化的版本,因此可以写成下面这个形式

k = sum (i,j);
h = sum (f,g);

如果两个参数的类型不同,编译器没办法自动推导
模板是很有用且通用的特性,可以有多个模板参数(模板本身也是完备的,实现模板元编程,在编译器完成计算,加快速度),还可以使用不是类型的参数

// function templates
#include <iostream>
using namespace std;

template <class T, class U>
bool are_equal (T a, U b)
{
  return (a==b);
}

int main ()
{
  if (are_equal(10,10.0))
    cout << "x and y are equal\n";
  else
    cout << "x and y are not equal\n";
  return 0;
}

自动推导了类型are_equal<int,double>(10,10.0)
这里不会产生歧义,因为数字字面量总是有特定的类型,除非指定了前缀,否则10永远是int,10.0永远是double。

非类型模板参数(Non-type template arguments)

不光可以用class或typename声明模板参数,基本类型也可以(需要是编译器常量,进行替换

// template arguments
#include <iostream>
using namespace std;

template <class T, int N>
T fixed_multiply (T val)
{
  return val * N;
}

int main() {
  std::cout << fixed_multiply<int,2>(10) << '\n';
  std::cout << fixed_multiply<int,3>(10) << '\n';
}

第二个模板参数是int,就可以一个函数参数差不多,可以直接使用
但是核心不同是:模板参数编译器就确定了,然后才能实例化,生成函数指令,所以一定不能是运行期指定,上面的两次调用实际上调用的是两个函数,两份指令,所以必须是常量

总结

模板和重载本质上是想解决同样的问题,泛型编程,不同的类型如果操作相同,可以用模板实现,不同也可以模板特化或者函数重载。
本质上模板实例化就是进行类型替换,因此替换点需要告诉模板参数类型,且能看到模板定义,所以模板通常以头文件提供,注意非参数类型的使用,最新C++支持string和float

posted @ 2023-05-26 10:30  xiaoweing  阅读(31)  评论(0)    收藏  举报