【从 C 向 C++ 进阶】- 类 - 24. 函数模版

从 C 向 C++ 进阶系列导航


1. 函数模版

C++ 中提出了函数模板的概念。函数模板可以看作是具备类型检查的宏,并可适配任意的数据类型。这也是泛型编程的思想,即不考虑具体数据类型的编程方式,具体的数据类型由调用者决定。

函数模板定义方式如下:

template <typename T>
Type Funname(T x)
{
    ...
}

其中,template 告诉编译器以下为模板的定义, 告诉编译器 T 为一个泛指类型。

  • 实验:
template <typename T>
T add(T num_A, T num_B)
{
	return num_A + num_B;
}

int main()
{
	int iSum = add<int>(1, 2);
	cout << "iSum = " << iSum << endl;							// iSum = 3
	
	float fSum = add<float>(0.3, 0.5);
	cout << "fSum = " << fSum << endl;							// fSum = 0.8
	
	string strSum = add<string>("123", "456");
	cout << "strSum = " << "\"" << strSum << "\"" << endl;		// strSum = "123456"
  
    return 0;
}

函数模版是支持类型自动推导的,如上 float fSum = add(0.3, 0.5); 中虽没有显式指明函数模版类型为 float,但编译器能够根据传参自动推导出函数模版类型为 float。

需要注意的是,当函数模板使用类型自动推导时,类型必须进行严格匹配,如果两传参类型不一致但函数模版的传参类型又一致时,会发生编译错误;显示类型指定时,能够进行隐式类型转换。


2. 函数模版本质

函数模板的本质为数据类型的替换。对于函数模板,编译器会进行二次编译。第一次为函数模板本身的编译,第二次为函数调用时进行数据类型替换后的函数编译。因此,每次的函数调用都会产生一个新的函数副本,这无疑会降低程序的编译效率,但使用模板技术可以大大地提高编程效率,所以牺牲较低的编译效率换取较高的编程效率是值得的。、

  • 实验:
typedef int(iFun)(int,int);
typedef float(fFun)(float,float);

template <typename T>
T sub(T num_A, T num_B)
{
	return num_A - num_B;
}

int main()
{
	int iDif = sub<int>(1, 2);
	cout << "iDif = " << iDif << endl;							// iDif = -1
	iFun* pi = sub<int>;
	cout << "pi = " << reinterpret_cast<void*>(pi) << endl;		// pi =  0x400a80
	
	float fDif = sub<float>(0.3, 0.5);
	cout << "fDif = " << fDif << endl;							// fDif = -0.2
	fFun* pf = sub<float>;
	cout << "pf = " << reinterpret_cast<void*>(pf) << endl;		// pf =  0x400a92
	
	// string strDif = sub<string>("123", "456");		// error: no match for ‘operator-’ (operand types are ‘std::__cxx11::basic_string<char>’ and ‘std::__cxx11::basic_string<char>’)
  
    return 0;
}

以上实验中,唯独使用 string 类型的模板时编译出错,这是因为在模板函数中使用了减法而 string 类型并没有减法相关的功能实现,所以在二次编译时会发生编译错误。而其余数据类型的函数模板并没有发生编译错误,且所调用的函数地址各不相同,这也证明了模板会进行二次编译。


3. 多参数函数模版

函数模板支持多个泛型参数,为从左向右部分指定类型参数。如果没有指定数据类型时,则以实参的数据类型为准。由于无法自动推导函数的返回类型,在工程中是将返回类型作为第一个类型参数。

  • 实验:
template <typename T1, typename T2, typename T3>
T1 add(T2 num_A, T3 num_B)
{
	return static_cast<T1>(num_A + num_B);
}

int main()
{
	int iSum = add<int, float, float>(1.1, 2.2);
	cout << "iSum = " << iSum << endl;							// iSum = 3

	float fSum = add<float, int>(1, 2.2);						// 等价于 add<float, int, float>(1, 2.2)
	cout << "fSum = " << fSum << endl;							// fSum = 3.2
	
	iSum = add<int>(1.1, 2.2);									// 等价于 add<int, float, float>(1.1, 2.2)
	cout << "iSum = " << iSum << endl;							// iSum = 3
  
    return 0;
}

4. 函数重载与函数模版

由于函数模板可以进行自动的类型推导,因此函数调用时有可能出现同时满足函数重载与函数模板的情况,此时编译器会优先调用重载后的函数。如果想指定调用函数模板,可以在函数调用处使用 “<>” 表示限定调用函数模板。

  • 实验:
template <typename T>
void Print(T num)
{
	cout << "void Print(T num): " << num << endl;
}

void Print(int num)
{
	cout << "void Print(int num): " << num << endl;
}

int main()
{
	Print(100);		    // void Print(int num): 100
	Print<>(100);	    // void Print(T num): 100
    Print(3.14);		// void Print(T num): 3.14
    
    return 0;
}
发布了60 篇原创文章 · 获赞 36 · 访问量 5933

猜你喜欢

转载自blog.csdn.net/qq_35692077/article/details/100109041
今日推荐