C++模版进阶

这篇具有很好参考价值的文章主要介绍了C++模版进阶。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


C++模版进阶,C/C++,c++,开发语言

C++模版进阶

1、非类型模版参数

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

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

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

#include <iostream>

using namespace std;

namespace xp {
    template<class T, size_t N = 10>
    class array {
    public:
        T &operator[](size_t pos) {
            return _array[pos];
        }

        bool empty() {
            return 0 == _size;
        }

    private:
        T _array[N];
        size_t _size;
    };
}

int main() {

    xp::array<int> arr;
    cout << arr[0] << endl; // 随机值
    cout << arr.empty() << endl;
    return 0;
}

  • 注意:
  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
  2. 非类型的模板参数必须在编译期就能确认结果。

2、模版的特化

2.1、概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板

template<class T>
bool Less(const T x, const T y) {
   return x < y;
}

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;
}

可以看到,Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果上述示例中,p1指向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,而比较的是p1和p2指针的地址,这就无法达到预期而错误。此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式模板特化中分为函数模板特化与类模板特化。


2.2、函数模版特化

  • 函数模板的特化步骤:
  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
template<class T>
bool Less(const T x, const T y) {
    return x < y;
}

// 函数模版特化
template<>
bool Less<Date*>(const Date* x, const Date* y) {
    return *x < *y;
}

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;
}

虽然解决了特殊情况特殊处理,但是感觉还是很鸡肋,因为我如果想比较两个Date*的数据,可以重载这个函数,如下:

bool Less(const Date* x, const Date* y) {
   return *x < *y;
}

这种代码更清晰明了。因此不建议使用函数模版。

  • 注意:函数模版不支持偏特化,默认就是全特化。

2.3、类模版特化

2.3.1、类模版全特化
  • 全特化即是将模板参数列表中所有的参数都确定化
template<class T1, class T2>
class Date1 {
public:
    Date1() {
        cout << "Date <T1 ,T2>" << endl;
    }

private:
    T1 _d1;
    T2 _d2;
};

// 类模版全特化
template<>
class Date1<char, int> {
public:
    Date1() {
        cout << "Date <char ,int>" << endl;
    }

private:
    int _d1;
    char _d2;
};

int main() {
    Date1<char, char> d1; // Date <T1 ,T2>
    Date1<char, int> d2; // Date <char ,int>
    return 0;
}

2.3.1、类模版偏特化
  • 偏特化即是任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类:
template<class T1, class T2>
class Date1 {
public:
    Date1() {
        cout << "Date <T1 ,T2>" << endl;
    }

private:
    T1 _d1;
    T2 _d2;
};
  • 仅部分特化类型

    template<class T>
    class Date1 <T,int>{
    public:
        Date1() {
            cout << "Date <T,int>" << endl;
        }
    
    private:
        T _d1;
        int _d2;
    };
    
  • 限制模版参数类型

    //两个参数偏特化为指针类型
    template<class T>
    class Date1 <T*,T*>{
    public:
        Date1() {
            cout << "Date <T*,T*>" << endl;
        }
       
    private:
        T* _d1;
        T* _d2;
    };
       
    //两个参数偏特化分别为引用类型和指针类型
    template<class T1, class T2>
    class Date1 <T1&,T2*>{
    public:
        Date1() {
            cout << "Date <T&,T*>" << endl;
        }
       
    private:
        T1 _d1;
        T2 _d2;
    };
       
    int main() {
        Date1<char, double> d0; // Date <T1 ,T2>
        Date1<double, int> d1; // Date <T,int>
        Date1<int *, int *> d2; // Date <T*,T*>
        Date1<int &, int *> d3; // Date <T&,T*>
        return 0;
    }
       
    

2.4、类模版特化示例

  • priority_queue.h文件

    #include <iostream>
    #include <vector>
    #include <list>
    #include <deque>
    #include <algorithm>
      
    using namespace std;
      
    class Date {
    public:
        // 获取某年某月的天数
        int GetMonthDay(int year, int month) {
            static int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
                                   31};
            int day = days[month];
            if (month == 2
                && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) {
                day += 1;
            }
            return day;
        }
    
    
    // 全缺省的构造函数
    //声明和定义分离,需要指定类域
        Date(int year, int month, int day) {
            if (year >= 0 && (month >= 1 && month <= 12) && (day <= GetMonthDay(year, month))) {
                _year = year;
                _month = month;
                _day = day;
            } else {
                cout << "初始化的日期有误" << endl;
                assert(year >= 0 && (month >= 1 && month <= 12) && (day <= GetMonthDay(year, month)));
            }
    
        }
    
    // 拷贝构造函数
    // d2(d1)
        Date(const Date &d) {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
    
    // 赋值运算符重载
    // d2 = d3 -> d2.operator=(&d2, d3)
        Date &operator=(const Date &d) {
            if (this != &d) {
                _year = d._year;
                _month = d._month;
                _day = d._day;
            }
            return *this;
        }
    
    // 析构函数
        ~Date() {
    //    cout << "~Date()" << endl;
        }
    
    // 日期+=天数  -- 改变原值
        Date &operator+=(int day) {
            //如果输入的day小于0
            if (day < 0) {
                *this -= -day;
                return *this;
            }
            _day += day;
            //加后的天数大于当月天数的最大值
            while (_day > GetMonthDay(_year, _month)) {
                _day -= GetMonthDay(_year, _month);
                _month++;
                if (_month == 13) {
                    _month = 1;
                    _year++;
                }
            }
            return *this;
        }
    
    // 日期+天数  --  不改变原值
        Date operator+(int day) {
            Date temp(*this);
            temp += day;
            return temp;
        }
    
    // 日期-=天数  --  改变原值
        Date &operator-=(int day) {
            //如果输入的day小于0
            if (day < 0) {
                *this += -day;
                return *this;
            }
            _day -= day;
            while (_day <= 0) {
                _month--;
                if (_month == 0) {
                    _year--;
                    if (_year == 0) {
                        printf("错误\n");
                        exit(-1);
                    }
                    _month = 12;
                }
                _day += GetMonthDay(_year, _month);
            }
            return *this;
        }
    
    // 日期-天数  --  不改变原值
        Date operator-(int day) {
            Date temp(*this);
            temp -= day;
            return temp;
        }
    
    
    // 前置++  --  先+1再计算
        Date &operator++() {
            *this += 1;
            return *this;
        }
    
    // 后置++  --  先计算再+1
        Date operator++(int) {
            Date temp(*this);//拷贝构造
            temp += 1;
            return temp;
        }
    
    // 前置--  --  先-1再计算
        Date &operator--() {
            *this -= 1;
            return *this;
        }
    
    // 后置--  --  先计算再-1
        Date operator--(int) {
            Date temp(*this);//拷贝构造
            temp -= 1;
            return temp;
        }
    
    
    // >运算符重载
        bool operator>(const Date &d) const {
            if (_year >= d._year) {
                if (_year > d._year)
                    return true;
                else {
                    //_year == d._year
                    if (_month >= d._month) {
                        if (_month > d._month)
                            return true;
                        else {
                            //_month == d._month
                            if (_day >= d._day) {
                                if (_day > d._day)
                                    return true;
                                else
                                    return false;
                            }
                        }
                    }
                }
            }
            return false;
        }
    
    // ==运算符重载
        bool operator==(const Date &d) const {
            return _year == d._year && _month == d._month && _day == d._day;
        }
    
    // >=运算符重载
        bool operator>=(const Date &d) const {
            return (*this > d) || (*this == d);
        }
    
    // <运算符重载
        bool operator<(const Date &d) const {
            return !(*this >= d);
        }
    
    // <=运算符重载
        bool operator<=(const Date &d) const {
            return (*this < d) || (*this == d);
        }
    
    // !=运算符重载
        bool operator!=(const Date &d) const {
            return !(*this == d);
        }
    
    
    // 日期-日期 返回天数
        int operator-(const Date &d) const {
            //假设第一个参数的日期更大
            int flag = 1;
            int count = 0;
            Date max = *this;
            Date min = d;
            if (*this < d) {
                flag = -1;
                max = d;
                min = *this;
            }
            while (max != min) {
                ++min;
                count++;
            }
            return count * flag;
        }
    
        friend ostream &operator<<(ostream &out, Date &d);
    
    private:
        int _year;
        int _month;
        int _day;
    };
    
    ostream &operator<<(ostream &out, Date &d) {
        out << d._year << " " << d._month << " " << d._day << endl;
        return out;
    }
    
    namespace xp {
    
        // 仿函数
        template<class T>
        class less {
        public:
            bool operator()(const T x, const T y) {
                return x > y;
            }
        };
        
        // 类模版偏特化
        template<class T1>
        class less<T1 *> {
        public:
            bool operator()(const T1 *const &x, const T1 *y) {
                return *x > *y;
            }
        };
        
        template<class T>
        class greater {
        public:
            bool operator()(const T x, const T y) {
                return x < y;
            }
        };
        
        // 类模版偏特化
        template<class T2>
        class greater<T2 *> {
        public:
            bool operator()(const T2 *x, const T2 *y) {
                return *x < *y;
            }
        };
        
        template<class T, class Container = vector<T>, class Compare= less<T> >
        class priority_queue {
            //  底层是一个堆
        public:
            // 假设默认是大根堆
        
            // 向上调整
            void Adjust_up(int child) {
                int parent = (child - 1) / 2;
                Compare com;
                while (child > 0) {
    //                if (_con[child] > _con[parent]) {
                    if (com(_con[child], _con[parent])) {
                        swap(_con[child], _con[parent]);
                        child = parent;
                        parent = (child - 1) / 2;
                    } else {
                        break;// 调整结束
                    }
                }
    
            }
        
            // 向下调整
            void Adjust_down(int parent) {
                Compare com;
                // 假设左孩子比右孩子更大
                int child = parent * 2 + 1;
                //如果右孩子存在
                if (child + 1 < _con.size() && com(_con[child + 1], _con[child])) {
                    ++child;
                }
        
                // 此时child是左右孩子值更大的那个
                while (child < _con.size()) {
                    if (com(_con[child], _con[parent])) {
                        swap(_con[child], _con[parent]);
                        parent = child;
                        child = parent * 2 + 1;
                    } else {
                        break;// 调整结束
                    }
                }
        
            }
        
            void push(const T &val) {
                _con.push_back(val);
                Adjust_up(_con.size() - 1);
            }
        
            void pop() {
                // 第一个元素和最后一个元素交换
                swap(_con[0], _con[_con.size() - 1]);
                _con.pop_back();
                Adjust_down(0);
            }
        
            const T &top() {
                return _con[0];
            }
        
            size_t size() const {
                return _con.size();
            }
        
            bool empty() {
                return _con.empty();
            }
        
        private:
            Container _con;
        };
    }
    
  • main.cpp文件

    #include <iostream>
    #include "priority_queue.h"
    
    using namespace std;
    
    
    int main() {
        Date *d1 = new Date(2024, 3, 11);
        Date *d2 = new Date(2024, 3, 13);
        Date *d3 = new Date(2024, 3, 10);
    
    
    //    xp::priority_queue<Date *> pq1;
        xp::priority_queue<Date *, vector<Date *>, xp::greater<Date *>> pq1;// 注意这里greater要指定命名空间
        pq1.push(d1);
        pq1.push(d2);
        pq1.push(d3);
    
        while (!pq1.empty()) {
            cout << *pq1.top();
            pq1.pop();
        }
        cout << "-------------\n";
    
        xp::priority_queue<int *> pq2;
        pq2.push(new int(1));
        pq2.push(new int(3));
        pq2.push(new int(2));
        pq2.push(new int(4));
    
        while (!pq2.empty()) {
            cout << *pq2.top() << endl;
            pq2.pop();
        }
    
        return 0;
    }
    
    

这里的优先队列里面就实现了就算是指针数据,也可以排序的功能。


3、模版的分离编译

3.1、 什么是分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。简单来说就是将函数或者类的声明和定义分离,比如声明放在.h文件,定义放在.cpp文件中。


3.2、模版的分离编译

看下面模版的声明和定义分离的情况

  • template.h文件
template<class T>
T Add(const T &x, const T &y);
  • template.cpp文件
#include "template.h"

template<class T>
T Add(const T &x, const T &y) {
    return x + y;
}
  • main.c文件
#include <iostream>
#include "template.h"

using namespace std;

int main() {
    cout << Add(1,2) << endl; // 出现链接错误
    return 0;
}

C++模版进阶,C/C++,c++,开发语言

问题出在:调用的地方知道怎么实例化但只有声明,定义的地方有模版但不知道怎么实例化cpp文件不会去扫描所有文件去确定实例化的内容。

解决办法:cpp文件模版显式实例化(鸡肋),或者对模版不进行分离编译,声明定义直接放在.h文件或者.hpp文件中

#include "template.h"

template<class T>
T Add(const T &x, const T &y) {
  return x + y;
}

// 显式实例化
template
int Add(const int &x, const int &y);

4、模版总结

  • 优点:
  1. 代码重用: 模板允许你编写通用的代码,可以用于不同类型的数据。这样可以避免编写大量类似的代码,提高代码的重用性。

  2. 类型安全: 使用模板可以在编译时进行类型检查,从而提高代码的类型安全性。模板可以确保在不同的场景下使用正确的类型。

  3. 灵活性: 模板允许你编写灵活的代码,因为模板的类型可以在编译时确定,而不是在运行时确定。这使得代码更加灵活,可以适应不同的需求。

  4. 标准库支持: C++ 标准库中大量的容器(如 std::vectorstd::list)、算法(如 std::sortstd::find)等都是使用模板实现的,这为开发人员提供了丰富的工具库。

  • 缺点:
  1. 编译时间: 使用模板可能会增加编译时间,特别是在模板实例化时会生成大量的代码。如果模板被频繁使用或者包含的头文件较多,编译时间可能会显著增加。

  2. 可读性: 模板代码可能会比非模板代码更加复杂,因为模板通常需要使用一些特殊的语法和技巧。这可能会降低代码的可读性,使得代码维护和调试更加困难。

  3. 错误消息: 当使用模板时,编译器生成的错误消息可能会变得更加复杂和晦涩,因为模板涉及到类型推断、模板参数推断等复杂的机制,导致错误消息不易理解。

  4. 代码膨胀: 模板会导致代码膨胀,因为每个模板实例化都会生成一份独立的代码。如果模板被频繁使用,可能会导致可执行文件的大小增加。

  5. 可移植性: 模板的实现在不同的编译器之间可能存在差异,这可能会影响代码的可移植性。某些特定的模板特性可能不被某些编译器支持,或者在不同编译器下的行为不同。


OKOK,C++模版进阶就到这里。如果你对Linux和C++也感兴趣的话,可以看看我的主页哦。下面是我的github主页,里面记录了我的学习代码和leetcode的一些题的题解,有兴趣的可以看看。

Xpccccc的github主页文章来源地址https://www.toymoban.com/news/detail-853527.html

到了这里,关于C++模版进阶的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】详细介绍模版初阶—函数模版、类模板

    ヾ(๑╹◡╹)ノ\\\" 人总要为过去的懒惰而付出代价 ヾ(๑╹◡╹)ノ\\\" 泛型编程 :编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。【不是针对某种类型】 template模版 template class T template typename T 模版的参数学习,可以类比函数参数。 模版参数

    2024年02月11日
    浏览(38)
  • C++模版

    泛型编程(Generic Programming)是一种编程范式,它强调编写可以在不同数据类型上工作的通用代码。泛型编程的目标是编写更具通用性和复用性的代码,以减少代码的重复编写,并提高代码的可维护性。 泛型编程的主要特点包括: 通用性: 泛型编程的代码不依赖于特定的数据

    2024年02月04日
    浏览(29)
  • C++模版初阶

    如下的交换函数中,它们只有类型的不同,应该怎么实现一个通用的交换函数呢? 使用函数重载虽然可以实现,但是有一下几个不好的地方:         1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数;       

    2024年02月05日
    浏览(39)
  • 【C++】模版初阶

    目录 泛函编程 函数模版 概念 格式 原理 实例化 模版函数的匹配原则 类模板 定义格式 如何实现一个通用的交换函数呢? 使用函数重载虽然可以实现,但是有几个不好的地方: 1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对

    2024年02月19日
    浏览(37)
  • C++模版简单认识与使用

    目录 前言: 1.泛型编程 2.函数模版 3.类模版 为什么要有类模版?使用typedef不行吗? 类模版只能显示实例化: 注意类名与类型的区别: 注意类模版最好不要声明和定义分离: 总结: 1.泛型编程 编写与类型无关的通用代码,是代码复用的一种手段,模版是泛型编程的基础。

    2024年04月17日
    浏览(31)
  • C++模版初阶讲解

    今日为大家分享C++的模版,这里先把模版的初阶讲解一下,以后再讲解模版进阶! 在我们进行学习的编程中,常常会有许多函数的功能相同,有些不同点可能就是其中的数据类型不同!如果我们统统进行函数重载的话,其实也能解决问题,例如,实现两个数的交换! 但是函

    2024年02月06日
    浏览(38)
  • 【C++深入浅出】模版初识

    目录 一. 前言 二. 泛型编程 三. 函数模版  3.1 函数模版的概念 3.2 函数模版的格式 3.3 函数模版的原理 3.4 函数模板的实例化 3.5 模板参数的匹配原则 四. 类模版 4.1 类模版的定义 4.2 类模版的实例化         本期我们要介绍的是C++的又一大重要功能---- 模版 。通过模版,我们

    2024年02月08日
    浏览(54)
  • 31 C++ 模版和泛型

    我们先来看一类问题,如下的三个方法能否换成一个方法呢? 这就是模版的来历。 所谓泛型编程,是以独立于 任何 特定类型的方式编写代码 这意味着,我们在声明或者定义的时候,不会指定具体的类型。 但是在使用的时候,需要指定具体的类型或者值 模版是泛型编程的基

    2024年02月02日
    浏览(39)
  • C++ 宽度优先搜索 || 模版题:走迷宫

    给定一个 n×m 的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1 ,其中 0 表示可以走的路,1 表示不可通过的墙壁。 最初,有一个人位于左上角 (1,1) 处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。 请问,该人从左上角移动至右下角 (n,m) 处,至

    2024年01月16日
    浏览(34)
  • C++ 单调队列 || 单调队列模版题:滑动窗口

    给定一个大小为 n≤106 的数组。 有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。 你只能在窗口中看到 k 个数字。 每次滑动窗口向右移动一个位置。 以下是一个例子: 该数组为 [1 3 -1 -3 5 3 6 7],k 为 3 。 窗口位置 最小值 最大值 [1 3 -1] -3 5 3 6 7 -1 3 1 [3 -1 -3] 5

    2024年01月20日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包