C++模板的注意事项

函数模板

template <typename T>        //把typename换成class也可以

函数模板调用方法

使用过程中, 有两种方法调用, 如下:

  • 自动类型推导:
#include <iostream>

template <class T>
void swap(T &first, T &second) {
    T temp;
    temp = first;
    first = second;
    second = temp;
}

int main() {
    int a = 10;
    int b = 20;
    swap(a, b);
    return 0;
}
  • 显示指定类型
#include <iostream>

template <class T>
void swap(T &first, T &second) {
    T temp;
    temp = first;
    first = second;
    second = temp;
}

int main() {
    int a = 10;
    int b = 20;
    swap<int>(a, b);
    return 0;
}

注意, 两者的区别在于调用时是否传入模板类型.

函数模板调用规则

  • 普通函数调用时可以发生隐式类型转换
  • 函数模板, 用自动类型推导, 不能发生隐式类型转换
  • 函数模板, 用显示指定类型, 可以发生隐式类型转换 

普通函数和函数模板调用顺序规则

假设有以下例子:

#include <iostream>
using namespace std;

void mSwap(int &first, int &second) {
    cout << "call normal function" << endl;
    int temp;
    temp = first;
    first = second;
    second = temp;
}

template <class T>
void mSwap(T &first, T &second) {
    cout << "call template function" << endl;
    T temp;
    temp = first;
    first = second;
    second = temp;
}
  • 如果函数模板和普通函数都可以调用, 优先调用普通函数

示例代码:

int main() {
    int a = 10;
    int b = 20;
    mSwap(a, b);
    return 0;
}

结果:

 

  • 可以通过空模板参数列表强制调用函数模板

示例:

int main() {
    int a = 10;
    int b = 20;
    mSwap<>(a, b);
    return 0;
}

结果:

  • 函数模板可以发生函数重载

比如又加了一个函数:

template <class T>
void mSwap(T &first, T &second) {
    cout << "call template function" << endl;
    T temp;
    temp = first;
    first = second;
    second = temp;
}

template <class T>
void mSwap(T &first, T &second, T &third) {
    cout << "call template function" << endl;
    T temp;
    temp = first;
    first = second;
    second = temp;
}

现在就有两个同名函数模板了, 这样传入三个参数就会调用下面的. 

  • 如果函数模板可以产生更好的匹配(不需要发生隐式类型转换), 优先调用函数模板

比如有如下调用:

int main() {
    char a = 'a';
    char b = 'b';
    mSwap(a, b);
    return 0;
}

运行结果为:

函数模板局限性

比如有如下代码:

class People {
public:
    People(string name, int age) : m_name(name), m_age(age) {}
    string m_name;
    int m_age;
};

template <class T>
bool mCompare(const T &a, const T &b) {
    if (a == b)
        return true;
    else
        return false;
}

 当有如下调用时:

int main() {
    People p1("michael", 18);
    People p2("michael", 18);
    mCompare(p1, p2);
    return 0;
}

会发生如下编译错误:

出现以上情况我们当然可以在类中实现" == "的运算符重载, 也可以用如下的方法, 在以上代码的基础上增加以下代码:

template<> bool mCompare(const People &a, const People &b) {
    if (a.m_name == b.m_name &&
        a.m_age  == b.m_age)
        return true;
    else
        return false;
}

这样即对以上的函数模板进行people定制化重载。

类模板

在类的声明按照如下声明:

template <class nameType, class ageType>
class People {
public:
    People(nameType name, ageType age) : m_name(name), m_age(age) {}
    nameType m_name;
    ageType m_age;
};

这样使用的时候按照如下使用:

int main() {
    People<string, int> p1("michael", 18);
    return 0;
}

类模板中成员函数创建时机

类模板中成员函数在调用时才创建.

如下测试代码:

class People1 {
public:
    void showPerple1(){
        cout << "this is class People1" << endl;
    }
};

class People2 {
public:
    void showPerple2(){
        cout << "this is class People2" << endl;
    }
};

template <class T>
class mClass {
public:
    void show1() {
        m_obj.showPerple1();
    }
    void show2() {
        m_obj.showPerple2();
    }
    T m_obj;
};

如上代码是可以编译通过的.

如下测试代码:

int main() {
    mClass <People1> p1;
    mClass <People2> p2;
    p1.show1();
    p2.show2();
    return 0;
}

 编译时候确定了T的类型之后, 如果发现T中没有成员函数中的函数, 就会报错.

类模板的对象作函数参数

假设有如下模板类:

template <class nameType, class ageType>
class People {
public:
    People(const nameType &name, const ageType &age) : m_name(name), m_age(age) {}
    void show() {
        cout << "name: " << m_name << endl;
        cout << "age: " << m_age << endl;
    }
    nameType m_name;
    ageType  m_age;
};

如果想要调用People实例化对象的show函数, 可以有以下方法:

  • 指定传入类型

方法如下:

void showPeople1(People<string, int> &p) {
    p.show();
}

int main() {
    People<string, int> p1("michael", 18);
    showPeople1(p1);
    return 0;
}
  • 参数模板化

方法如下:

template <class nameType, class ageType>
void showPeople2(People<nameType, ageType> &p) {
    p.show();
}

int main() {
    People<string, int> p2("michael", 18);
    showPeople1(p2);
    return 0;
}
  • 整个类模板化

方法如下:

template <class P>
void showPeople3(P &p) {
    p.show();
}

int main() {
    People<string, int> p3("michael", 18);
    showPeople3(p3);
    return 0;
}

类模板继承

  • 当父类是模板类时, 子类在继承的时候需要指定父类中的模板类型, 当有如下代码:
template <class T>
class Father {
public:
    T m_T;
};

class Son : public Father {
public:
};

这样编译会报错, 需要改为:

template <class T>
class Father {
public:
    T m_T;
};

class Son : public Father<int> {
public:
};

这样就可以了 

  • 也可以把子类也变成模板类, 可以按照如下操作:
template <class T>
class Father {
public:
    T m_T;
};

template <class T1, class T2>
class Son : public Father<T2> {
public:
    T1 m_T1;
};

类外实现

当有如下类:

template <class nameType, class ageType>
class People {
public:
    People(nameType name, ageType age);
    void showPeople();
    nameType m_name;
    ageType m_age;
};

当我们在类外实现其构造函数和成员函数时, 需要按照如下方式:

//constructor
template <class nameType, class ageType>
People<nameType, ageType>::People(nameType name, ageType age) {
    m_name = name;
    m_age = age;
}

//member function
template <class nameType, class ageType>
void People<nameType, ageType>::showPeople() {
    cout << "name: " << m_name << endl;
    cout << "age: " << m_age << endl;
}

类模板的分文件编写

类模板与友元

函数模板和类模板的区别

  • 类模板是不存在类型推导的, 所以实例化对象的时候必须传入类型.
  • 类模板是可以有默认参数的

可参考如下代码:

template <class nameType, class ageType = int>
class People {
public:
    People(nameType name, ageType age) : m_name(name), m_age(age) {}
    nameType m_name;
    ageType m_age;
};

int main() {
    People<string> p1("michael", 18);
    return 0;
}

这样是可以的.

如果两个参数都有默认类型, 实例化对象的时候也必须要有尖括号, 如下: 

template <class nameType = string, class ageType = int>
class People {
public:
    People(nameType name, ageType age) : m_name(name), m_age(age) {}
    nameType m_name;
    ageType m_age;
};

int main() {
    People <>p1("michael", 18);
    return 0;
}

模板的特化

模板的设计是为了有效减少代码量,但是某些情况,对于某种特定类型,模板就需要进行特化。

全特化和偏特化

全特化就是限定死模板实现的具体类型,偏特化就是如果这个模板有多个类型,那么只限定其中的一部分。

函数模板的特化

函数模板只能进行全特化不能进行偏特化,函数模板的全特化可以参照上文说的,正常函数就是相当于对函数模板的全特化。

类模板的特化

template<typename T1, typename T2>
class Test
{
public:
    Test(T1 i,T2 j):a(i),b(j){cout<<"模板类"<<endl;}
private:
    T1 a;
    T2 b;
};

template<>
class Test<int , char>
{
public:
    Test(int i, char j):a(i),b(j){cout<<"全特化"<<endl;}
private:
    int a;
    char b;
};

template <typename T2>
class Test<char, T2>
{
public:
    Test(char i, T2 j):a(i),b(j){cout<<"偏特化"<<endl;}
private:
    char a;
    T2 b;
};

那么下面3句依次调用类模板、全特化与偏特化:

Test<double , double> t1(0.1,0.2);  
Test<int , char> t2(1,'A');  
Test<char, bool> t3('A',true);  
//依次打印:
//类模板
//全特化
//偏特化
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值