C++模板之——函数模板详解及代码示例

简介: C++模板之——函数模板详解及代码示例

一、模板特点

  • 模板只是一个框架,不可以直接使用
  • 目的是为了提高代码复用性,将类型参数化

二、分类

C++提供两种模板机制:函数模板类模板。是一种泛型编程思想。

2.1 函数模板

语法:

// 作用:建立一个通用函数,其函数返回值类型和参数类型可以不具体指定,用一个虚拟的类型来表示。
template<typename T>
函数声明或者定义

说明:

template —— 表明要创建模板。

typename —— 表示一种数据类型,可以用 class 代替, T 就是具体的名称,通常为大写字母。

函数模板使用的注意事项

  • 自动类型推导,必须能推导出一致的数据类型
  • 模板使用前,必须要能确定出T的类型
2.1.1 代码示例

函数模板的两种调用方式:自动类型推导显式调用

#include <iostream>
using namespace std;
// 函数模板,提供一个通用的相加函数
template<typename T> // 声明一个模板
T add(T a, T b){ return a+b; }
// 普通函数,相同逻辑代码冗余
int add(int a, int b){ return a+b; }
double add(double a, double b){ return a+b; }
// 模板使用前,必须要能确定出T的类型
template<typename T>
void func(){ cout << "func called." << endl; }
int main(int argc, char* argv[])
{
  // 1. T自动类型推导
  cout << "==============自动类型推导================" << endl;  
    cout << "add(int, int): " << add(2, 3) << endl;
    cout << "add(double, double): " << add(2.1, 3.1) << endl;
  // 2. 显式的指定T类型
  cout << "==============显式指定类型==============" << endl;
    cout << "add(int, int): " << add<int>(2, 3) << endl;
    cout << "add(double, double): " << add<double>(2.1, 3.1) << endl;
    cout << "==============注意事项==============" << endl;
    // cout << "add(int, int): " << add(2, 3.1) << endl; // 错误,自动推导出的T类型不一致: add(int, double)
    // func(); // 错误,因为该函数没有使用T的地方,所以必须先指定T的类型
    func<int>();
  return 0;
}

运行结果:

2.1.2 函数模板与普通函数的区别

主要区别是:能否发生隐式类型转换

  • 普通函数可以发生隐式类型转换
  • 函数模板在使用自动类型推导时不能发生隐式类型转换,使用显式指定类型时可以,建议使用第二种
#include <iostream>
using namespace std;
// 函数模板
template<typename T>
T add(T a, T b){ return a+b; }
// 普通函数
int add(int a, int b){ return a+b; }
double add(double a, double b){ return a+b; }
int main(int argc, char* argv[])
{
    int a = 10;
    char c = 'c';
    cout  << "普通函数: " << add(a, c) << endl;
    // cout  << add(a, c) << endl; // 如果使用函数模板,编译器不知道将T该确定为 int 还是 char,编译报错
    cout  << "函数模板:" << add<int>(a, c) << endl;
  return 0;
}

运行结果:

2.2.3 函数模板和普通函数的调用规则
  1. 规则一:如果函数模板和普通函数都可以实现,有限调用普通函数
  2. 规则二:可以通过空模板参数列表来强制使用函数模板
  3. 规则三:函数模板也能发生重载
  4. 规则四:如何函数模板能发生更好的匹配,优先调用函数模板
#include <iostream>
using namespace std;
template<typename T>
void MyPrint(T a, T b) { cout  << "调用函数模板." << endl; }
template<typename T> // 函数模板重载
void MyPrint(T a, T b, T c) { cout  << "调用函数模板重载." << endl; }
void MyPrint(int a, int b) { cout  << "调用普通函数." << endl; }
int main(int argc, char* argv[])
{
    int a = 10, b = 20;
    MyPrint(a, b);  // 满足规则一:优先调用普通函数
    MyPrint<>(a, b); // 满足规则二:强制调用函数模板
    MyPrint(a, b, 100); // 满足规则三
    char A = 'A', B = 'B'; // 满足规则四:优先调用函数模板,因为普通函数还需要将char隐式的转为int,函数模板直接确定char
    MyPrint(A, B);
  return 0;
}

运行结果:

2.2.3 函数模板的局限性
template<class T>
void func(T a, T b){
    a = b;
}
template<class T>
void compare(T a, T b){
    if (a == b){
      //do something
    }
}

问题1:func函数模板提供的作用是将b的值赋给a。但是,如果T传的是一个数组,就无法实现。

问题2:compare函数模板提供的作用是比较a与b的值。但是,如果T传的是类,也无法比较。

为了解决这类问题,C++提供了可以为特定的类型提供具体化的模板。

#include <iostream>
#include <string>
using namespace std;
class Person{
public:
    Person(std::string name, int age){
        this->m_name = name;
        this->m_age = age;
    }
public:
    std::string m_name;
    int m_age;
};
template<class T>
bool MyCompare(T &a, T &b){
    return a == b ? true : false;
}
// 为Person类专门提供具体化的模板,会被优先调用
template<> bool MyCompare(Person& p1, Person& p2)
{
    if (p1.m_name == p2.m_name && p1.m_age == p2.m_age)
    {
        return true;
    }
    else
    {
        return false;
    }
}
int main(int argc, char* argv[])
{
    Person p1("panda", 20);
    Person p2("panda", 20);
    bool res = MyCompare(p1, p2);
    if (res) {
        std::cout << "p1 == p2" << std::endl;
    }
    else{
        std::cout << "p1 != p2" << std::endl;
    }
    return 0;
}

文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习:

相关文章
|
10天前
|
算法 安全 C++
提高C/C++代码的可读性
提高C/C++代码的可读性
28 4
|
1月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
1月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
16 1
|
1月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
46 6
|
1月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
25 0
C++ 多线程之线程管理函数
|
1月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
229 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
1月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
42 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
4天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
24 5
|
11天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
40 4
|
12天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
35 4