14.模板基础知识

模板基础知识

概念

​ C++中的模板是一种通用编程工具,允许你编写与数据类型无关的代码。模板允许你编写函数或类,其中的某些部分可以用类型或值替换,从而实现通用性和灵活性。模板是C++中强大而灵活的特性之一,它们使得编写可重用的代码变得更加容易。

​ 在C++中,有两种主要类型的模板:函数模板和类模板。

函数模板

​ 函数模板允许你编写一个通用的函数,其中的某些类型可以由用户指定。以下是一个简单的函数模板的示例:

template <typename T>
T add(T a, T b) {
    return a + b;
}

​ 在这个例子中,<typename T> 表示一个模板参数,T 是一个占位符,表示类型。函数模板 add 可以接受两个相同类型的参数,并返回它们的和。

​ 使用示例:

cout << "add(3, 4) = " << add(3, 4) << endl;   // 实例化为 add<int>(3, 4);
cout << "add(3.2, 4.2) = " << add(3.2, 4.2) << endl;   // 实例化为 add<double>(3.2, 4.2);

​ 在这个示例中我们只能传入相同数据类型的元素,那我们能不能传入不同数据类型的元素嘞?我们来试一下:

/*************************************************************************
        > File Name: test.cpp
        > Author:Xiao Yuheng
        > Mail:3312638794@qq.com
        > Created Time: Thu Nov  9 15:07:26 2023
 ************************************************************************/

#include <iostream>

using namespace std;

template<typename T, typename U>
T add(T a, U b) {
    return a + b;
}

int main() {
    cout << "add(3, 4) = " << add(3, 4) << endl;
    cout << "add(3.2, 4.2) = " << add(3.2, 4.2) << endl;
    cout << "add(3, 4.2)" << add(3, 4.2) << endl;
    cout << "add(4.2, 3)" << add(4.2, 3) << endl;
    return 0;
}

​ 我们在这里定义两个模板类型,这样是不是就可以接收两种不同类型的数据了,我们来运行一下:

在这里插入图片描述

​ 这里我们可以发现

    cout << "add(3, 4.2)" << add(3, 4.2) << endl;
    cout << "add(4.2, 3)" << add(4.2, 3) << endl;

​ 这两行代码的输出结果竟然是不一样的,这是什么原因嘞,我们来看一下这个返回值的类型,没错当返回值类型Tdouble类型时,结果就是正确的,当返回值类型Tint时,返回值会自动保留整数部分。这时返回的结果就不是我们想要的,我们要怎么解决这个问题呢?我们接着往下看:

decltype的使用

decltype 是C++11引入的关键字,用于获取表达式的类型。它的作用是告诉编译器,根据给定表达式的类型来声明一个变量或定义一个返回类型。

​ 语法格式如下:

decltype(expression)

​ 其中 expression 是一个任意的表达式。

​ 我们来使用一下:

/*************************************************************************
        > File Name: test.cpp
        > Author:Xiao Yuheng
        > Mail:3312638794@qq.com
        > Created Time: Thu Nov  9 15:07:26 2023
 ************************************************************************/

#include <iostream>

using namespace std;

template<typename T, typename U>
decltype(T() + U()) add(T a, U b) {
    return a + b;
}

int main() {
    cout << "add(3, 4) = " << add(3, 4) << endl;
    cout << "add(3.2, 4.2) = " << add(3.2, 4.2) << endl;
    cout << "add(3, 4.2)" << add(3, 4.2) << endl;
    cout << "add(4.2, 3)" << add(4.2, 3) << endl;
    return 0;
}

​ 我们把返回值类型换成decltype(T() + U()),因为我们无法使用变量a和变量b所以我们直接调用这两个类型的构造函数,我们来运行一下看看结果:

在这里插入图片描述

​ 没有问题,可是这时又出现一个问题,如果着个函数没有默认构造呢?如下这种情况:

/*************************************************************************
        > File Name: test.cpp
        > Author:Xiao Yuheng
        > Mail:3312638794@qq.com
        > Created Time: Thu Nov  9 15:07:26 2023
 ************************************************************************/

#include <iostream>

using namespace std;

class A {
public:
    A() = delete;
    A(int x) : x(x) {}
    int x;
};

class B {
public:
    B() = delete;
    B(int x) : x(x) {}
    int x;
};

class C {
public:
    C() = delete;
    C(int x) : x(x) {}
    int x;
};

C operator+(const A &a, const B &b) {
    return a.x + b.x;
}

C operator+(const B &a, const A &b) {
    return a.x + b.x;
}

ostream &operator<<(ostream &out, const C &c) {
    out << c.x;
    return out;
}

template<typename T, typename U>
decltype(T() + U()) add(T a, U b) {
    return a + b;
}

int main() {
    cout << "add(3, 4) = " << add(3, 4) << endl;
    cout << "add(3.2, 4.2) = " << add(3.2, 4.2) << endl;
    cout << "add(3, 4.2)" << add(3, 4.2) << endl;
    cout << "add(4.2, 3)" << add(4.2, 3) << endl;
    A a(123);
    B b(456);
    cout << "add(a, b) = " << add(a, b) << endl;
    return 0;
}

​ 就比如这段代码,我们自定义了两个类,但是呢?这两个类的默认构造被我删掉了,所以这个模板在这个时候就不成立了,我们怎么才能让他成立呢?如下:

template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

​ 我们可以把返回值写在后面,这样我们就可以访问a对象和b对象了,前面用auto来占位,这样我们就完美解决了这个问题。

类模板

类的使用

/*************************************************************************
        > File Name: test.cpp
        > Author:Xiao Yuheng
        > Mail:3312638794@qq.com
        > Created Time: Thu Nov  9 15:53:48 2023
 ************************************************************************/

#include <iostream>

using namespace std;

template<typename T>
class PrintAny {
public:
    PrintAny(ostream &out) : out(out) {}
    PrintAny &Print(T a) {
        out << a;
        return *this;
    }
    PrintAny &endl() {
        out << std::endl;
        return *this;
    }

private:
    ostream &out;
};

int main() {
    PrintAny<int> pint(cout);
    PrintAny<double> pdouble(cout);
    PrintAny<string> pstring(cout);
    pint.Print(3).endl();
    pdouble.Print(6.5).endl();
    pstring.Print("xiaoyuheng").endl();
    return 0;
}
1. 模板类 PrintAny

这是一个模板类,接受一个模板类型 T。类中有一个私有成员 ostream &out,它是一个对输出流的引用,用于确定数据输出的目标。构造函数 PrintAny(ostream &out) 初始化这个输出流。

类中有两个成员函数:

  • PrintAny &Print(T a):接受一个类型为 T 的参数 a,将其输出到 out 引用的输出流中,并返回 *this 以支持链式调用。
  • PrintAny &endl():输出一个换行符到 out 引用的输出流中,并返回 *this 以支持链式调用。
2. main 函数:

main 函数中,我们实例化了三个不同类型的 PrintAny 对象:

  • PrintAny<int> pint(cout);:用于打印整数。
  • PrintAny<double> pdouble(cout);:用于打印双精度浮点数。
  • PrintAny<string> pstring(cout);:用于打印字符串。

然后,我们使用相应的对象调用 Print 函数来输出数据,并使用 endl 函数输出换行。

3. 使用示例:
cppCopy codepint.Print(3).endl();                // 输出整数 3,并换行
pdouble.Print(6.5).endl();           // 输出双精度浮点数 6.5,并换行
pstring.Print("xiaoyuheng").endl();  // 输出字符串 "xiaoyuheng",并换行

这样的设计使得你可以通过不同的实例来处理不同类型的数据,而不需要为每个类型都编写一个特定的输出函数。通过模板类的设计,可以提高代码的可重用性和通用性。

完美来运行一下,看看运行结果:

在这里插入图片描述

成员函数的使用

/*************************************************************************
        > File Name: test.cpp
        > Author:Xiao Yuheng
        > Mail:3312638794@qq.com
        > Created Time: Thu Nov  9 15:57:11 2023
 ************************************************************************/

#include <iostream>

using namespace std;

class PrintAny {
public:
    PrintAny(std::ostream &out) : out(out) {}
    template<typename T>
    PrintAny &Print(T a) {
        out << a;
        return *this;
    }
    PrintAny &endl() {
        out << std::endl;
        return *this;
    }

private:
    ostream &out;
};

int main() {
    PrintAny p(cout);
    p.Print(3).endl().Print(4.3).endl().Print("xiaoyuheng").endl();
    return 0;
}
1. 类 PrintAny

这是一个用于在不同类型上输出信息的类。与之前的版本不同,这里的 PrintAny 类本身不是一个模板类,但它包含了一个函数模板 Print

  • PrintAny(std::ostream &out):构造函数,接受一个 ostream 引用,用于确定数据输出的目标。
  • template<typename T> PrintAny &Print(T a):函数模板,接受一个类型为 T 的参数 a,将其输出到 out 引用的输出流中,并返回 *this 以支持链式调用。
  • PrintAny &endl():输出一个换行符到 out 引用的输出流中,并返回 *this 以支持链式调用。
2. main 函数:

main 函数中,我们实例化了一个 PrintAny 对象 p,并使用它来输出不同类型的数据:

cppCopy code
p.Print(3).endl().Print(4.3).endl().Print("xiaoyuheng").endl();

这一行代码展示了链式调用的方式,通过调用 Printendl 函数,可以在一行中输出整数、浮点数和字符串,并在每次输出后换行。

3. 输出示例:
plaintextCopy code3
4.3
xiaoyuheng

his` 以支持链式调用。

2. main 函数:

main 函数中,我们实例化了一个 PrintAny 对象 p,并使用它来输出不同类型的数据:

cppCopy code
p.Print(3).endl().Print(4.3).endl().Print("xiaoyuheng").endl();

这一行代码展示了链式调用的方式,通过调用 Printendl 函数,可以在一行中输出整数、浮点数和字符串,并在每次输出后换行。

3. 输出示例:
plaintextCopy code3
4.3
xiaoyuheng

这是程序的输出结果,每个数据后面都有一个换行符。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值