模板特化与类型萃取(1)

一、从案例中理解什么是模板特化

1、通过一个案例理解模板特化

(1)案例前奏:写一个swap函数库,可以适用于各种数据类型。结论是模板比函数重载好用。

#include <iostream>

using namespace std;

template <typename X>//定义一个抽象类型X,在函数被调用时确定具体类型
void myswap(X& a, X& b)//使用swap作为函数名会与某个库函数名冲突
{
    X tmp;

    tmp = a;
    a = b;
    b = tmp;
}

int main(int argc, char *argv[])
{
    int a =3, b = 4;
    double c = 3.3, d = 4.4;

    myswap(a, b);
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;

    myswap(c, d);
    cout << "c = " << c << endl;
    cout << "d = " << d << endl;

    return 0;
}

(2)案例:写一个GreaterThan函数,可以对比各种数据类型的大小。
(3)特殊要求:int等比较数值大小,但string类型对比时,不比较字典序,而是以字符串长短来比较

#include <iostream>
using namespace std;

template <typename T>
bool GreaterThan(T a, T b)//模板泛化函数
{
    cout << "template GreaterThan" << endl;
    if (a > b)
        return true;
    else
        return false;
}

template<>
bool GreaterThan(string a, string b)//模板特化函数
{
    cout << "special template GreaterThan" << endl;
    if (a.size() > b.size())
        return true;
    else
        return false;
}

bool GreaterThan(string a, string b)//普通函数
{
    cout << "ordinary GreaterThan" << endl;
    if (a.size() > b.size())
        return true;
    else
        return false;
}

int main(int argc, char *argv[])
{
    cout << boolalpha << GreaterThan<int>(5, 3) << endl;
    cout << boolalpha << GreaterThan<int>(3, 5) << endl;
    cout << boolalpha << GreaterThan<double>(5.4, 5.3) << endl;
    cout << boolalpha << GreaterThan<double>(5.3, 5.4) << endl;
    
    //下面所传的两个参数为const型,不可修改,GreaterThan()函数调用优先级为模板泛化函数最高
    cout << boolalpha << GreaterThan<string>("linux", "harmony") << endl;//执行special
    cout << boolalpha << GreaterThan("linux", "harmony") << endl;

    //当比较的两个参数为string类型时,上述三个函数优先级为:
    //普通函数 > 模板特化函数 > 模板泛化函数
    string a = "linux", b = "harmony"; 
    cout << boolalpha << GreaterThan<string>(a, b) << endl;//执行special
    cout << boolalpha << GreaterThan(a, b) << endl;

    return 0;
}

参考学习:C++普通函数与模板函数以及特化函数重载的优先级问题

2、模板特化总结

(1)模板特化有点类似于函数重载,而且都是编译链接时确定,而非运行时确定的。

(2)特化,specialize,就是让模板参数T在某个具体类型时可以特殊化指定处理

(3)特化的模板声明,前面一般是 template<>

二、偏特化和全特化

1、全特化与偏特化概念

(1)全特化,特化原模板的所有模板类型为具体类型

(2)偏特化,又叫局部特化,特化原模板的部分类型,或部分特化原模板的类型

(3)全特化比较简单,而偏特化更复杂,是之后讨论的重点。

2、函数模板的全特化

(1)代码实践,单个模板参数
  上边提供的那个程序中就实现了单个模板参数的全特化。
(2)代码实践,多个模板参数

#include <iostream>
using namespace std;

template <typename T1, typename T2>
void print(T1 a, T2 b)//模板泛化函数
{
    cout << "template function" << endl;
    cout << "a = " << a << "; b = " << b << endl;
}

template<>
void print(int a, string b)//函数模板的全特化
{
    cout << "complete special function" << endl;
    cout << "a = " << a << "; b = " << b << endl;
}

int main(int argc, char*[])
{
    string a = "one", b = "two";    
    print(1, 2);
    print(a, b);
    print(1, a);

    return 0;
}

3、类模板的全特化

(1)代码实践,单个模板参数

#include <iostream>
using namespace std;

template <typename T>
class people
{
public:
    T attribute;
    people(){};
    people(T attr){attribute = attr;};
    void print(T a);
};

template <typename T> void people<T>::print(T a)
{
    cout << "template function print: " << a << endl;
}

template <>
class people<int>
{
public:
    int attribute;
    people(){};
    people(int attr){attribute = attr;};
    void print(int a);    
};
void people<int>::print(int a)
{
    cout << "complete special template function print: " << a << endl;
}

int main(int argc, char *argv[])
{
    people<int> p1(10);
    people<string> p2("linux");

    p1.print(10);
    p2.print("linux");

    return 0;
}

(2)代码实践,多个模板参数

#include <iostream>
using namespace std;

template <typename T1, typename T2>
class people
{
public:
    T1 attribute;
    people(){};
    people(T1 attr){attribute = attr;};
    void print(T2 a);
};

template <typename T1, typename T2> void people<T1, T2>::print(T2 a)
{
    cout << "template function print: " << a << endl;
}

template <>
class people<int, string>
{
public:
    int attribute;
    people(){};
    people(int attr){attribute = attr;};
    void print(string a);    
};
void people<int, string>::print(string a)
{
    cout << "complete special template function print: " << a << endl;
}

int main(int argc, char *argv[])
{
    people<int, string> p1(10);
    people<string, int> p2("linux");

    p1.print("linux");
    p2.print(10);

    return 0;
}

三、类模板的多种偏特化

1、类模板的第一种偏特化

(1)特化多个模板参数中的一部分参数
(2)这种比较简单,代码实践演示

#include <iostream>
using namespace std;

template <typename T1, typename T2>
class people//模板泛化类
{
public:
    void print(T1 a, T2 b);
};

template <typename T1, typename T2> void people<T1, T2>::print(T1 a, T2 b)
{
    cout << "template class" << endl;
    cout << "a = " << a << "; b = " << b << endl;
}

template <>
class people<string, int>//类模板的偏特化
{
public:
    void print(string a, int b)
    {
        cout << "partial template class:people<string, int>" << endl;
        cout << "a = " << a << "; b = " << b << endl;
    }

};

template <>
class people<int, string>//类模板的偏特化
{
public:
    void print(int a, string b)
    {
        cout << "partial template class:people<int, string>" << endl;
        cout << "a = " << a << "; b = " << b << endl;
    }

};

int main(int argc, char *argv[])
{
    people<int, int> p1;
    people<string, string> p2;
    people<int, string> p3;
    people<string, int> p4;

    p1.print(1, 1);
    p2.print("two", "two");
    p3.print(3, "three");
    p4.print("four", 4);

    return 0;
}

2、类模板的第二种偏特化

(1)特化为T的指针类型
(2)这种特化理解起来稍微有点绕(可以将T理解为一个万能类型,而T则表示这个类型必须为指针,T包含T,所以T*是T的偏特化),实战演示

#include <iostream>
using namespace std;

template <typename T1, typename T2>
class people//模板泛化类
{
public:
    void print(T1 a, T2 b);
};

template <typename T1, typename T2> void people<T1, T2>::print(T1 a, T2 b)
{
    cout << "template class" << endl;
    cout << "a = " << a << "; b = " << b << endl;
}

template <typename T>
class people<T *, int>//类模板的偏特化
{
public:
    void print(T a, int b)
    {
        cout << "partial template class:peoplepeople<T *, int>" << endl;
        cout << "a = " << a << "; b = " << b << endl;
    }

};

template <typename T>
class people<T *, string>//类模板的偏特化
{
public:
    void print(T *a,  string b)
    {
        cout << "partial template class:peoplepeoplepeople<T *, string>" << endl;
        cout << "a = " << a << "; b = " << b << endl;
    }

};

int main(int argc, char *argv[])
{
    int a = 0, b = 1;
    string c = "two";
    char d = 'd';

    people<int, int> p1;
    people<char *, int> p2;
    people<int *, string> p3;
    people<string *, int> p4;

    p1.print(1, 1);
    p2.print(d, 2);
    p3.print(&a, "three");
    p4.print("four", 4);

    return 0;
}

3、类模板的第三种偏特化

(1)特化为T的其他类模板,譬如vector
(2)这种特化理解起来难度更大,实战演示

#include <iostream>
#include <vector>
#include <array>
using namespace std;

template <typename T1, typename T2>
class people//模板泛化类
{
public:
    void print(T1 a, T2 b);
};

template <typename T1, typename T2> void people<T1, T2>::print(T1 a, T2 b)
{
    cout << "template class" << endl;
    cout << "a = " << a << "; b = " << b << endl;
}

template <typename T>
class people<vector<T>, int>//类模板的偏特化
{
public:
    void print(T a, int b)
    {
        cout << "partial template class:people<vector<T>, int>" << endl;
        cout << "a = " << a << "; b = " << b << endl;
    }

};

template <typename T>
class people<array<T, 3>, string>//类模板的偏特化
{
public:
    void print(array<T, 3> a,  string b)
    {
        cout << "partial template class:people<array<T, 3>, string>" << endl;
    }

};

template <typename T>
class people<T& , string>//类模板的偏特化
{
public:
    void print(T& a,  string b)
    {
        cout << "partial template class:people<T& , string>" << endl;
    }

};

int main(int argc, char *argv[])
{
    int a = 1;
    vector<int> b;
    array<int, 3> c;

    people<int, int> p1;
    people<int &, string> p2;
    people<vector<int>, int> p3;
    people<array<int, 3>, string> p4;

    p1.print(1, 1);
    p2.print(a, "int&");
    p3.print(3, 3);
    p4.print(c, "array<int, 3>");

    return 0;
}

4、类模板的第四种偏特化

(1)特化为带const的版本

#include <iostream>
using namespace std;

template <typename T>
class People
{
public:
	void func(void)
	{
		cout << "template class" << endl;
	}
};


template<typename T>
class People<const T &>
{
public:
	void func(void)
	{
		cout << "People<const T &>" << endl;
	}
};

int main(int argc, char *argv[])
{
    People<string> p1;
    People<const char &> p2;
    People<const int &> p3;

    p1.func();
    p2.func();
    p3.func();

    return 0;
}

四、函数模板为什么不能偏特化

1、事实

(1)函数模板确实不支持偏特化,只能全特化,这是编译器决定的(若想求证,可自行写个程序测试一下,比较简单,下面是我的测试程序)

#include <iostream>
using namespace std;

template <typename T>
void func(T a)//模板泛化函数
{
    cout << "template:void func(T a) " << endl;
    cout << a <<  endl;
}

template <>
void func(int a) //全特化函数
{
    cout << "complete template :void func(int a) " << endl;
    cout << a <<  endl;
}

#if 0
template <typename T>
void func<T*>(T a)//偏特化函数,但函数没有偏特化,故编译不会通过
{
    cout << "partial template:void func(T* a) " << endl;
    cout << a <<  endl;
}
#endif

// 用函数重载来实现T *的偏特化完全相同的效果
template<typename T>
void func(T *a)
{
	cout << "func(T *a), a = " << a << endl;
}


void func(int a) //非模板函数,即普通函数
{
    cout << "ordinary:void func(int a) " << endl;
    cout << a <<  endl;
}

int main(int argc,char* argv[])
{
    int a = 5;

    func(3.14);    //template
    func(5);       //ordinary
    func<int>(5);  //complete template
    //func<int *>(5);//用于调用偏特化函数,但函数无偏特化
    func<int>(&a);//重载函数:func(T *a)
    func(&a);     //重载函数:func(T *a)
    func("linux");//重载函数:func(T *a)

    return 0;
}

(2)怎么办?
  用模板函数重载即可

(3)总结:为什么函数模板不能偏特化?
  因为没必要支持,模板函数重载就能搞定

2、分析深度原因

(1)C++语言设计基本原则:后出现的语法尽量兼容且不破坏原有的语法规则

(2)C++一开始就支持函数重载,所以模板函数自然沿用了支持函数重载

(3)偏特化实现的效果,完全可以用模板函数重载实现,所以没必要让模板函数可以偏特化

(4)再思考:类模板为什么可以偏特化?
  因为类不能重载

五、编译器匹配规则和特化的总结

1、编译器匹配规则

(1)第1步先匹配非模版函数,也就是普通函数,如果匹配到就执行,匹配不到进入下一步

(2)第2步再匹配基础泛化版函数,如果匹配不到就报错了,匹配到进入下一步

(3)第3步再匹配完全特化版本,如果匹配到就执行,匹配不到就执行上一步匹配到的泛化版本

(4)一个小细节:函数模板的特化(当然是全特化)不参与函数重载

2、特化与递归结合

(1)特化与递归结合,可以很巧妙的实现编译期的条件判断

#include <iostream>
 
template<int i>
void print()
{
  print<i-1>();
  std::cout << i << std::endl;
}
 
//特例,终止递归。
template<>
void print<1>()
{
  std::cout << 1 << std::endl;
}
 
int main()
{
  print<100>();//在编译期展开相当于100条输出语句
  //即调用void print()并传参100,print<i-1>();开始递归,i初值100
}

(2)详见:https://blog.csdn.net/liuxuejiang158blog/article/details/17678573

3、特化的最后总结

(1)特化本质上是我们顶替了编译器的工作,我们帮编译器做了类型推导

(2)模板特化和模板实例化这2个概念的对比

(3)全特化本质上是一个实例,而偏特化本质上还是一个模板,只是原来模板的一个子集,所以全特化的函数模板,本质上是实例(但不参与普通函数的重载),从而不会与函数模板产生二义性

注:本文章参考了《朱老师物联网大讲堂》课程笔记,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小嵌同学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值