[C++]模板总结

定义

template开始,后跟一个模板参数列表(在模板定义中,模板参数列表不能为空)

typename是用来定义模板参数关键字(C++98之后),也可以 使用class(切记:不能使用struct代替class)

实例化

隐式实例化:让编译器根据实参推演模板参数的实际类型,编译器一般不会进行类型转换操作,形参和实参类型必须完全匹配。

显式实例化:在函数名后的<>中指定模板参数的实际类型。如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

非类型模板参数

用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用

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

模板参数的匹配原则

对于非模板函数和同名函数模板,如果其他条件都相同,在隐式实例化时会优先调用非模板函数,不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。

函数模板特化

// 必须要先有一个基础的函数模板
template<class T>
bool IsEqual(T& left, T& right) {
    return left == right;
}

// 关键字template后面接一对空的尖括号<>,函数名后跟一对尖括号,尖括号中指定需要特化的类型
template<>
bool IsEqual<char*>(char*& left, char*& right) {
    // 函数形参表必须和模板函数的基础参数类型相同
    if (strcmp(left, right) == 0)
        return true;
    return false;
}

一般情况下为了实现简单通常都是将函数直接给出

类模板

类模板中函数放在类外进行定义时,需要加模板参数列表。

类模板实例化需要在类模板名字后跟<>,实例化的类型放在<>中,类模板名字不是真正的类,而实例化的结果才是真正的类。

类模板特化

template<typename T1, typename T2>
class Data {};

// 全特化
template<>
class Data<int, char> {};

// 部分特化
template<typename T1>
class Data<T1, int> {};

// 限制特化
template<typename T1, typename T2>
class Data<T1*, T2*> {};

类型萃取

实现Copy函数,使其可以拷贝任意类型

单使用memcpy,可以拷贝内置类型,如果涉及到深拷贝(比如string),就会出错。

单使用循环赋值,效率很低

通过增加bool参数判断是否内置类型,就可将两种拷贝的优势结合。但用户需要根据所拷贝元素的类型传递第三个参数,那出错的可能性就增加。

所以需要通过类型萃取来判断是否内置类型,但需要将所有类型遍历一遍,每次比较都是字符串的比较,效率比较低。

bool IsPODType(const char* strType) {
    const char* arrType[] = { "char", "short", "int", "long", "long long", "float",
"double", "long double" };
    for (size_t i = 0; i < sizeof(arrType) / sizeof(arrType[0]); ++i) {
        if (strcmp(strType, arrType[i]) == 0)
            return true;
    }
    return false;
}

template<class T>
void Copy(T* dst, const T* src, size_t size) {
    if (IsPODType(typeid(T).name())) // RTTI
        memcpy(dst, src, sizeof(T)*size);
    else {
        for (size_t i = 0; i < size; ++i)
            dst[i] = src[i];
    }
}

一般写法:

// 内置类型
struct TrueType {
    static bool Get() { return true; }
};

// 代表自定义类型
struct FalseType {
    static bool Get() { return false; }
};

template<class T>
struct TypeTraits {
    typedef FalseType IsPODType;
};

template<>
struct TypeTraits<char> {
    typedef TrueType IsPODType;
};
template<>
struct TypeTraits<int> {
    typedef TrueType IsPODType;
};
// ... 将所有内置类型都特化

template<class T>
void Copy(T* dst, const T* src, size_t size) {
    if (TypeTraits<T>::IsPODType::Get()) // RTTI
        memcpy(dst, src, sizeof(T)*size);
    else {
        for (size_t i = 0; i < size; ++i)
            dst[i] = src[i];
    }
}

简便写法:

struct TrueType {};
struct FalseType {};

template <class T>
struct TypeTraits {
    typedef FalseType IsPodType;
};

template<>
struct TypeTraits<char> {
    typedef TrueType IsPODType;
}; 
template<>
struct TypeTraits<int> {
    typedef TrueType IsPODType;
};
// ... 将所有内置类型都特化
 
template <class T>
void _Copy(T* dst, T* src, size_t size, TrueType) {
    memcpy(dst, src, sizeof(T)* size);
}
 
template <class T>
void _Copy(T* dst, T* src, size_t size, FalseType) {
    for (size_t i = 0; i < size; ++i) {
        dst[i] = src[i];
    }
}

template <class T>
void Copy(T* dst, T* src, size_t size) {
	_Copy(dst, src, size, TypeTraits<T>::IsPodType()); // RTTI
}

模板分离编译

如果将模板的声明放在.h文件中,将其定义放在其他.cpp中,在main.cpp中实例化就会出现链接错误。

因为头文件不参与编译,编译器对工程中的多个源文件单独编译,然后将多个obj文件链接。

在定义的.cpp文件中编译器找不到对模板的实例化,不会生成对应的函数。

所以在main.cpp中call找不到对应函数的地址。

有两种方法可以解决:

  1. 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的
  2. 模板定义的位置显式实例化,这种方法不推荐使用

总结

模板复用了代码,更快迭代开发,增强了代码的灵活性,但是会导致代码膨胀问题,编译时间变长,出现模板编译错误时,不易定位错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值