【C++】模板进阶

简介: 【C++】模板进阶

1.非类型模板参数

模板参数分为 类型形参 和 非类型形参

类型形参即出现在模板参数列表中, 跟在class或者typename之类的参数类型名称

非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将参数当成常量使用

#include<iostream>
using namespace std;
#define N 10
template <class T>//类型模板参数
class Array
{
public:
private:
    T _a[N];
};
int main()
{
    Array<int> a;//10
    Array<double>b;//100
    return 0;
}

使用类型模板参数,虽然看似可以解决问题,但若a要10个int的数组,b要100个double的数组,宏就没办法解决了


所以为了解决这个问题,引入非类型模板参数,定义的是常量,一般是整形常量

#include<iostream>
using namespace std;
template <class T,size_t N>//非类型模板参数
class Array
{
public:
private:
    T _a[N];
};
int main()
{
    Array<int,10> a;
    Array<double,20>b;
    return 0;
}

同样非类型模板参数还可以给缺省值 ,由于是整型常量,所以缺省值也是常量


同样在函数内部,无法对常量值进行修改

#include<iostream>
using namespace std;
template<class T,size_t N=20>
void func(const T& a)
{
    N = 20;//左操作数必须为左值
}
int main()
{
    func(1);
}

必须为整形常量 整形包括 int 、char、 long 、long long 、short

若为double类型就会报错

2. 模板特化

使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理

函数模板特化


#include<iostream>
using namespace std;
class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year)
        , _month(month)
        , _day(day)
    {}
    bool operator<(const Date& d)const
    {
        return (_year < d._year) ||
            (_year == d._year && _month < d._month) ||
            (_year == d._year && _month == d._month && _day < d._day);
    }
    bool operator>(const Date& d)const
    {
        return (_year > d._year) ||
            (_year == d._year && _month > d._month) ||
            (_year == d._year && _month == d._month && _day > d._day);
    }
    friend ostream& operator<<(ostream& _cout, const Date& d)
    {
        _cout << d._year << "-" << d._month << "-" << d._day;
        return _cout;
    }
private:
    int _year;
    int _month;
    int _day;
};
template<class T>
bool Less(T left, T right)
{
    return left < right;
}
int main()
{
    cout << Less(1, 2) << endl; // 可以比较,结果正确
    Date d1(2022, 7, 7);
    Date d2(2022, 7, 8);
    cout << Less(d1, d2) << endl; // 可以比较,结果正确
    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << Less(p1, p2) << endl; // 可以比较,结果错误
    return 0;
}

若p1与p2都为Date*,则调用Less ,会变成 left与right指针的比较 ,不符合预期,我们想要的是日期与日期之间的比较


所以为了防止这种 特殊情况的发生,所以使用 模板特化 (对某些类型进行特殊化处理)

此时就可以正常比较两个日期


函数模板特化意义不大,可以直接使用函数重载的方式来实现的

类模板特化

偏特化

偏特化——进一步的限制

必须在原模版的基础上才可以使用

针对的是指针这个泛类

全特化

必须在原模版的基础上才可以使用

只针对其中一个,如指针中的日期类

半特化

对部分参数特化

template<class T1, class T2>
class Data
{
public:
    Data() { cout << "Data<T1, T2>" << endl; }
private:
    T1 _d1;
    T2 _d2;
};
//半特化
template <class T1>
class Data<T1, int>//只将第一个模板参数特化
{
public:
    Data() { cout << "Data<T1, int>" << endl; }
private:
    T1 _d1;
    int _d2;
};
void TestVector()
{
    Data<int, int> d1;//Data<T1, int>
    Data<int*, int> d3;
//Data<T1, int>
    Data<double, int> d4;
//Data<T1, int>
}
int main()
{
    TestVector();
    return 0;
}

此时虽然有两个参数,但是只特化了一个 ,无论传的是 int、int*、double 都是走到半特化

参数的进一步限制

两个参数特化为引用类型

两个参数特化为指针类型

3. 模板的分离编译

模板并不支持分离编译 即声明在一个文件,定义在一个文件

此时调用模板add 就会报错,因为模板的声明和定义不在同一文件中

调用普通函数func,即可正常运行

模板会发生链接错误

func.h func.cpp test.cpp

预处理:头文件展开/ 注释的消除/ 条件编译/ 宏的替换


编译: 检查语法,生成汇编代码

func.s test.s

汇编:汇编代码转换为二进制机器码

func.o test.o

链接:合并生成可执行文件

a.out

由预处理阶段到 编译阶段时,通过func.i生成func.s ,生成汇编指令 ,但是只能生成func的,不能生成add的

函数被编译后生成指令,才可以找到第一句指令的地址 即函数地址


func会被编译成一堆指令,所以在func.o有func函数的地址,但是没有add的地址,因为add没有实例化 ,没办法确定T

就像是你差一点就可以交首付了,你打电话给你哥们借钱,你哥们说没问题,就像声明一样,这是一种承诺,

所以在编译阶段 add就可以过了 即call add( ? )

?代表现在没有地址

等到链接阶段才能拿到地址 ,而现在只是说得到承诺,到时候可以去拿到地址啦

但是 当你要交首付的时候,你哥们突然说借不了钱了,那这个房子首付也就交不上了

就好比 链接时 找不到add的函数地址了,所以会链接错误

链接之前不会交互,各走各的,不知道不能实例化,因为没办法确定T

解决链接错误的问题

显示实例化

虽然设置成double可以解决double的问题,但是传int 等又会报错,所以还是有很大的缺陷

局限性,实际中一般不使用

将声明和定义放在一个文件中

此时在预处理阶段将头文件展开,由于声明和定义都在一起,所以此时add函数 call时有地址存在,

不需要向func一样,链接时去寻找函数地址了

声明和定义放在一起,直接就可以实例化,编译时就有地址,不需要链接

4. 模板总结

缺陷

1. 模板会导致代码膨胀问题,也会导致编译时间变长

模板会进行实例化,所以运行时间变长,因为是一个模板,若传过来int 、char、double 类型都要实现,所以代码会膨胀


2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

通常为一报错就一大堆错误,一般只需要找到第一个错误并解决它,就可以了

————————————————

版权声明:本文为CSDN博主「风起、风落」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_62939852/article/details/129735680

相关文章
|
2月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
122 10
|
4月前
|
编译器 C++
【C++】——初识模板
【C++】——初识模板
【C++】——初识模板
|
1月前
|
安全 编译器 C++
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
53 4
|
1月前
|
算法 编译器 C++
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
36 3
|
5月前
|
程序员 C++
C++模板元编程入门
【7月更文挑战第9天】C++模板元编程是一项强大而复杂的技术,它允许程序员在编译时进行复杂的计算和操作,从而提高了程序的性能和灵活性。然而,模板元编程的复杂性和抽象性也使其难以掌握和应用。通过本文的介绍,希望能够帮助你初步了解C++模板元编程的基本概念和技术要点,为进一步深入学习和应用打下坚实的基础。在实际开发中,合理运用模板元编程技术,可以极大地提升程序的性能和可维护性。
|
1月前
|
编译器 C++
【c++】模板详解(1)
本文介绍了C++中的模板概念,包括函数模板和类模板,强调了模板作为泛型编程基础的重要性。函数模板允许创建类型无关的函数,类模板则能根据不同的类型生成不同的类。文章通过具体示例详细解释了模板的定义、实例化及匹配原则,帮助读者理解模板机制,为学习STL打下基础。
33 0
|
2月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
20 1
|
2月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
50 9
|
2月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
70 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
2月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
96 2