模板与泛型编程
了解隐式接口和编译器多态
classes和templates都支持接口和多态。
对classes而言接口是显示的,以函数签名为中心。多态是通过virtual函数发生于运行时期。
对templates而言接口是隐式的,基于有效表达式。多态是通过templates特化和函数重载解析发生于编译时期。
了解typename的双重意义
声明模板时,前缀关键字
class
和typename
可互换。
请使用关键字typename
标识嵌套从属类型名称;但不得在基类列和成员初值列内以它作为基类修饰符。
当我们声明模板类型参数,class
和typename
意义完全相同。然而C++并不总是把class
和typename
视为等价。有时候你一定得使用typename
。
当使用模板时,如果需要访问某个依赖于模板参数的嵌套类型,必须在该类型前使用typename
来明确指明这是一个类型。
template<typename T>
void Foo()
{
typename T::SubType *ptr;//明确是SubType是一个类型而不是一个变量
}
例外是不得在基类列和成员初值列内以它作为基类修饰符。
GPT:这些例外是因为在这些语境中,编译器已经能够清楚地识别出名称指向的是类型,不需要
typename
关键字来消除歧义。
学习处理模板化基类的名称
CRE:在派生类模板中访问基类模板的成员。这通常涉及到以下几种方法。
- 在基类函数调用动作之前加上
this->
。 - 使用
using
声明式。 - 明白指出被调用的函数位于基类内。
将与参数无关的代码抽离templates
模板生成多个类和多个函数,所以任何模板代码都不该与某个造成膨胀的模板参数产生相依关系。
因非类型模板参数造成的代码膨胀,往往可以消除,做法是以函数参数或class成员变量替换template参数。
因类型参数而造成的代码膨胀,往往可以降低,做法是让带有相同二进制表述的具现类型共享实现码。
关于相同二进制表述:例如大多数平台上,所有的指针类型都有相同的二进制表述,所以可以共用唯一一份底层实现。(可以让
void*
完成实际工作)(有些C++标准库为vector、list等模板做了这件事)
运用成员函数模板接受所有兼容类型
请使用成员函数模板生成“可接受所有兼容类型”的函数。
如果你声明成员模板用于“泛型的复制构造函数”或者“泛型的赋值操作符”,你还是需要声明正常的复制构造函数和赋值操作符。(因为声明复制构造模板/赋值操作符模板不会阻止编译器生成默认的复制构造函数/赋值操作符)
template<typename T>
class SmartPtr{
public:
template<typename U>
SmartPtr(const SmartPtr<U>& other);//不用声明为explicit,因为要模拟原始指针的行为,而原始指针之间转换是隐式的。
//其他类型的指针转换....
}
需要类型转换时请为模板定义非成员函数
当我们编写一个类模板,而它所提供的“与此模板相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“类模板内部的friend函数”。
示例:
template<typenameT>
class Rational{
template <typename T>
cosnt Rational<T> operator* (const Rational<T>& lhs, const Rational<T>& rhs){...}
}
Rational<int> onHalf(1, 2);
Rational<int> result = onHalf * 2;//编译错误
编译器要特化operator*
之前,必须先算出T是什么。问题是它们没有这个能力。
在模板实参推导过程中从不将隐式类型转换函数纳入考虑。
只要利用一个事实,我们就可以缓和编译器在模板实参推导方面受到的挑战:模板类内部的friend声明式可以指涉某个特定函数。
template<typenameT>
class Rational{
friend cosnt Rational operator* (const Rational& lhs, const Rational& rhs);
}
template <typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs)
{.....}
请使用 traits classes 表现类型信息
- 总结:
traits classes 使得“类型相关信息”在编译期可用。他们以模板和“模板特化”完成实现。
整合重载技术后,traits classes 有可能在编译期对类型执行if..else测试。
关于traits:
GPT:在C++中,traits class是一种用于编译时元编程的技术,通常用于获取某些类型的信息。traits class的常见用途是通过模板特化来定义类型的特性或行为。
GPT:traits class的基本概念是提供一个模板类,它包含一些可以在编译时确定的信息,如类型别名、常量等。通过特化这个模板类,可以为不同的类型定义不同的特性。
- 示例:
// 定义一个traits class模板
template <typename T>
struct is_integer { static constexpr bool value = false; };
// 针对整数类型进行特化
template <>
struct is_integer<int> { static constexpr bool value = true; };
template <>
struct is_integer<short> { static constexpr bool value = true; };
template <>
struct is_integer<long> { static constexpr bool value = true; };
- 配合函数重载实现编译时期判断:
template <typename T>
typename std::enable_if< std::is_integral<T>::value, void >::type
process(T value) {
std::cout << "Processing integer: " << value << std::endl;
}
其中std::enable_if的实现类似:
template<bool B, typename T = void>
struct enable_if {};
// 特化,当 B 为 true 时定义 type
template<typename T>
struct enable_if<true, T> {
typedef T type;
};
认识模板元编程
模板元编程优点之一是:它让某些事情更容易,没有它那些事情是苦难甚至不可能的。
模板元编程优点之二是:可将工作由运行时期移往编译期,因而得以实现早期错误侦测和更高的执行效率。
模板元编程的缺点是:会让编译时间变长。
模板元编程是图灵完备的。
(END)