【二十一】【C++】模版特化

模版的参数

类型模板参数

类型模板参数允许在定义模板时指定一个占位符,这个占位符在模板实例化时将被具体的类型替换。这使得我们能够编写与类型无关的通用代码。例如,标准库中的 std::vector<T> 使用一个类型模板参数 T,这意味着你可以有一个 std::vector<int>std::vector[std::string](std::string) 或任何其他 std::vector<T>,其中 T 是实际的类型。

 
template<class T>
class MyContainer {
    //...
};

在这个例子中,T 是一个类型模板参数,用于在类内部表示一个未知的类型。

非类型模板参数

非类型模板参数允许你将值(而不是类型)作为参数传递给模板。这些参数必须是编译时常量表达式,因为模板实例化发生在编译时。非类型模板参数可以是整数、枚举、指向函数或对象的指针、引用、std::nullptr_t 或者浮点数(C++20起)。

 
template<int N>
class FixedSizeArray {
    int array[N]; // 使用非类型参数N定义数组大小
};

在这个例子中,N 是一个非类型模板参数,用于定义固定大小数组的大小。

模板参数列表

模板参数列表可以包含类型模板参数和非类型模板参数的任意组合,它们按照声明时的顺序进行匹配。

 
template<class T, int Size>
class MyArray {
    T array[Size]; // 结合使用类型模板参数和非类型模板参数
};

在这个例子中,MyArray 既有一个类型模板参数 T,也有一个非类型模板参数 Size。这允许 MyArray 在实例化时指定数组的元素类型以及数组的大小。

日期类指针比较大小,错误结果

 
/*日期类指针比较大小,错误结果*/
#if 1
#include <iostream>
using namespace std;

class Date {
        int _year;
        int _month;
        int _day;
    public:
        Date() {}
        Date(int year, int month, int day)
            : _year(year)
            , _month(month)
            , _day(day)
        {}
        bool operator<(const Date&  right) {
            if (this->_year < right._year) {
                return true;
            } else if (this->_year == right._year && this->_month < right._month) {
                return true;
            } else if (this->_year == right._year && this->_month == right._month && this->_day < right._day) {
                return true;
            }

            return false;
        }
        bool operator==(const Date& right) {
            return this->_year == right._year && this->_month == right._month && this->_day == right._day;
        }
        bool operator>(const Date& right) {
            return !(*this < right) && !(*this == right);
        }
 };

template<class T>
bool Less(T left, T right) {
    return left < right;
 }

int main() {
    cout << Less(1, 2) << endl;
    Date d1(2024, 1, 1);
    Date d2(2024, 1, 2);

    cout << Less(d1, d2) << endl;

    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << Less(p1, p2) << endl;



 }
#endif

Date

Date 类用于表示日期,包含年(_year)、月(_month)和日(_day)三个私有成员变量。类中定义了几个成员函数:

构造函数:一个默认构造函数和一个接受年、月、日为参数的构造函数。

operator<:重载 < 操作符,用于比较两个 Date 对象。比较逻辑是首先比较年份,如果年份相同,则比较月份,月份相同则比较日。这保证了日期的正确顺序比较。

operator==:重载 == 操作符,用于检查两个 Date 对象是否相等,即年、月、日都相同。

operator>:重载 操作符,基于 <== 操作符的结果来判断一个 Date 对象是否大于另一个。

Less 模板函数

Less 是一个函数模板,接受两个参数并返回它们使用 < 操作符比较的结果。这个模板函数可以用于任何定义了 < 操作符的类型。

main 函数

main 函数中,进行了三种不同的比较:

  • 比较两个整数值12,使用 Less 函数,输出结果为 1true),表示 1 小于 2

  • 创建两个 Date 对象 d1d2,分别代表 2024年1月1日2024年1月2日,使用 Less 函数比较这两个对象,输出结果为 1true),表示 d1 日期小于 d2

  • 创建两个 Date 对象的指针 p1p2,分别指向 d1d2,然后尝试使用 Less 函数比较这两个指针。这里的 Less 函数比较的是指针的值(即内存地址),而不是指针所指向的 Date 对象的值。因此,输出的结果(truefalse)依赖于这两个对象在内存中的存储位置,与日期的逻辑顺序无关。

最后一次调用 Less 函数比较两个指针时,结果可能会引起混淆,因为它没有按照预期比较 Date 对象的逻辑大小,而是比较了它们的内存地址。这通常不是我们想要的行为。

模版特化概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果。模板特化是 C++ 中的一个功能,它允许为模板提供特定类型的特殊实现。这在需要为某些特定类型或值集合提供优化或特殊行为时非常有用。

函数模版特化解决 日期类指针比较大小

 
/*使用模版特化解决特殊情况*/
#if 1
#include <iostream>
using namespace std;

class Date {
        int _year;
        int _month;
        int _day;
    public:
        Date() {}
        Date(int year, int month, int day)
            : _year(year)
            , _month(month)
            , _day(day)
        {}
        bool operator<(const Date&  right) {
            if (this->_year < right._year) {
                return true;
            } else if (this->_year == right._year && this->_month < right._month) {
                return true;
            } else if (this->_year == right._year && this->_month == right._month && this->_day < right._day) {
                return true;
            }

            return false;
        }
        bool operator==(const Date& right) {
            return this->_year == right._year && this->_month == right._month && this->_day == right._day;
        }
        bool operator>(const Date& right) {
            return !(*this < right) && !(*this == right);
        }
 };

template<class T>
bool Less(T left, T right) {
    return left < right;
 }

template<>
bool Less<Date*>(Date* left, Date* right) {
    return *left < *right;
 }

int main() {
    cout << Less(1, 2) << endl;
    Date d1(2024, 1, 1);
    Date d2(2024, 1, 2);

    cout << Less(d1, d2) << endl;

    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << Less(p1, p2) << endl;



 }
#endif

 
 
template<>
bool Less<Date*>(Date* left, Date* right) {
    return *left < *right;
 }

Less 模板函数(特化版本):

Date*(指向 Date 类的指针)特化了 Less 模板函数。这个特化版本通过解引用指针来比较指向的 Date 对象,确保当使用指针类型调用 Less 时,能够正确比较 Date 对象的内容,而不是它们的地址。

函数模版特化步骤

实现通用模板

首先我们需要实现一个通用的函数模版。先有一个通用的函数模版版本,才可能有对于这个函数模版特殊化处理的版本。

 
template<class T>
bool Less(T left, T right) {
    return left < right;
 }

全特化

  • 关键字template后面接一对空的尖括号<>

  • 函数名后跟一对尖括号,尖括号中指定需要特化的类型。

  • 函数形参表: 必须要和模板函数的基础参数类型完全相同。

 
template<>
bool Less<Date*>(Date* left, Date* right) {
    return *left < *right;
 }

注意

  • C++ 不允许函数模板的偏特化,因为函数模板可以通过重载来实现相同的效果。

  • 特化必须在通用模板的声明之后和任何使用特化模板的代码之前进行。

  • 函数模版不建议特化,因为我们可以使用函数重载代替特化。

使用特化版本

当我们调用Less(p1,p2)时,检测到p1p2属于Date*类型,那么会与特化版本相匹配,并调用对应类型的特化版本函数。

类模版特化

模板特化的类型

全特化(Full Specialization):为模板的所有参数提供具体的类型或值。这意味着你为特定的模板实例化提供了一个完全定制的实现。全特化对类模板和函数模板都适用。

偏特化(Partial Specialization):仅为模板的部分参数提供具体的类型或值。偏特化允许你针对模板的某些方面提供特殊实现,而不是全部。注意,偏特化仅适用于类模板,函数模板不支持偏特化,但可以通过重载实现类似的效果。

类模版全特化

 
/*类模版全特化*/
#if 1
#include<iostream>
using namespace std;

template<class T1,class T2>
class Date{
public:
    Date(){cout<<"Date<T1,T2>"<<endl;}
private:
    T1 _d1;
    T2 _d2;
 };

template<>
class Date<int,char>{
public:
    Date(){cout<<"Date<int,char>"<<endl;}
private:
    int _d1;
    char _d2;
 };

int main(){
    Date<int,int> d1;
    Date<int ,char> d2;
    
}
#endif

  • 对于 d1,它是基于通用模板类 Date 的实例化,因为它使用的类型参数(int, int)没有对应的全特化版本。因此,其构造函数输出 "Date<T1,T2>"

  • 对于 d2,它匹配了为类型参数组合 int, char 提供的全特化版本,所以其构造函数输出 "Date<int,char>"

偏特化

 
/*类模版偏特化*/
#if 1
#include <iostream>
using namespace std;

template<class T1, class T2>
class Date {
    public:
        Date() {
            cout << "Date<T1,T2>" << endl;
        }
    private:
        T1 _d1;
        T2 _d2;
 };

template<class T1>
class Date<T1, int> {
    public:
        Date() {
            cout << "Date<T,int>" << endl;
        }
    private:
        T1 _d1;
        int _d2;
 };


template<typename T1, typename T2>
class Date<T1*, T2*> {
    public:
        Date() {
            cout << "Date<T1*,T2*>" << endl;
        }
    private:
        T1* _d1;
        T2* _d2;
 };

template<typename T1, typename T2>
class Date< T1&, T2&> {
    public:
        Date(const T1&d1, const T2&d2)
            : _d1(d1)
            , _d2(d2) {
            cout << "Date<const T1&,const T2&>" << endl;
        }
    private:
        const T1& _d1;
        const T2& _d2;
 };

int main() {
    Date<double, int>d1;
    Date<int, double>d2;
    Date<int*, int*>d3;
    Date<int&, int&>d4(3, 4);
 }
#endif

  • d1 触发了第一个偏特化版本 Date<T,int>

  • d2 使用了基本模板,因为它不匹配任何偏特化版本。

  • d3 触发了第二个偏特化版本 Date<T1*,T2*>,正确地处理了指针类型的参数。

  • d4 触发了第三个偏特化版本 Date<T1&,T2&>,正确地处理了引用类型的参数。

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妖精七七_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值