c++11对模板添加了很多内容,如对函数模板默认参数的支持,模板别名
一、模板别名
具体用法:using
别名
=需要替代的模板
,使用模板别名替代长模板能极大地提高代码的简洁性和清晰度,但是如果滥用模板别名会导致代码的可读性降低,所以建议只在长模板时使用,例如简单的vector<int>不应该使用别名,使用别名反而让人无法第一时间了解意图且完全没必要
使用示例:
#include <iostream>
#include <string>
#include <map>
template<typename T>
using StringMap = std::map<std::string, T, std::less<>>;
int main()
{
StringMap<int> myMap;
myMap["apple"] = 5;
myMap["cherry"] = 8;
myMap["banana"] = 4;
for (auto& pair : myMap) {
std::cout << pair.first << " :" << pair.second << std::endl;
}
std::cout << "\n";
StringMap<std::string> anotherMap;
anotherMap["dog"] = "woof";
anotherMap["cat"] = "meow";
anotherMap["bird"] = "tweet";
for (const auto& pair : anotherMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
二、函数模板默认参数
在C++11以前只能使用类模板默认参数,但是在C++11中添加了对函数模板默认参数的支持,函数模板的参数推导允许您在调用函数时省略显式的模板参数列表。这在编写泛型代码时特别有用,因为它减少了需要手动指定的类型信息。然而,需要注意的是,当模板参数不能完全从函数参数推导出来时,您仍然需要显式指定这些参数。例如,当函数模板有多个模板参数,但只有部分参数可以从实参推导时,剩下的参数需要显式指定。
template <typename T = int>
class MyClass {
// ...
};//正确
// 在C++98或C++03中,这是不允许的
template <typename T = int>
void myFunction() {
// ...
}
函数模板的使用
#include <iostream>
#include <string>
#include <map>
template <typename T>
class base {
public:
void print(T a) {
std::cout << a << std::endl;
}
};
template <typename T = int>
class child {
public:
void print(T a) {
std::cout << a << std::endl;
}
};
template <typename T>
void function1(T a)
{
std::cout << a << std::endl;
}
template <typename T = int>
void function2(T a)
{
std::cout << a << std::endl;
}
template <typename R,typename T>
R function3(T a)//因为无法通过T直接推导出R,所以无法实现像普通函数一样调用,只能显示显示调用
{
return a;
}
template <typename R = int,typename T>//默认参数写在参数列表最后,这是与普通函数的区别之一
R function4(T a)
{
return a;
}
int main()
{
function1("nihao");//自动推导
function2("aha");//自动推导
base<std::string> mybase;
mybase.print("nihao"); //输出nihao
child<> mychild;//使用默认参数int
mychild.print(10); //输出10
auto result1 = function3<double>(10); // 明确指定返回类型为 double
auto result2 = function4(10); // 返回值使用默认参数 int,T 被推导为 int
return 0;
}
我们能够观察到,模板函数有些情况下是能够像普通函数一样被调用的(只要函数模板的所有模板参数都能被推导出来,那么该模板函数就能像普通函数一样调用,否则就需要给无法推导的参数显式的指定类型
),但模板类即使每一个参数都有默认参数也必须写成带<>的形式,即模板实例,因为类模板本身只是一个模板,它定义了如何生成具体类型的蓝图,当您实例化一个模板类时,编译器根据模板参数生成具体的类类型。使用尖括号也是告诉编译器您正在创建一个模板类的实例,而不是使用一个普通类或者访问一个静态成员。
三、需要注意的点
1. 默认参数的区别:与普通函数的默认参数不同,第一,函数模板的默认参数是对类型的默认设定,而不是对函数参数值的默认设定,第二,函数模板的默认参数可以出现在模板参数列表中的任何位置,而不像普通函数的默认参数只能放在参数列表的末尾。
2. 模板参数的自动推导:当调用一个函数模板时,C++ 编译器会尝试根据提供的实参自动推导模板参数的类型。如果无法推导出类型,编译器会回退到使用默认模板参数(如果有的话)。如果既无法推导出类型,也没有默认模板参数,编译器则会报错。
3. 模板实例化:对于类模板,即使所有模板参数都提供了默认值,你仍然需要使用尖括号 <> 来实例化模板(即使尖括号内没有内容)。这是因为编译器需要明确知道这是一个模板实例化的操作。
4.模板代码的性能考虑:虽然模板提供了极大的灵活性和强大的泛型能力,但滥用模板可能导致编译时间显著增长,特别是在大型项目中。过度复杂的模板元编程也可能使得最终的二进制文件体积变大。合理使用模板,并在必要时倾向于简单、清晰的设计