zhouqijie

模板与泛型编程



了解隐式接口和编译器多态

classes和templates都支持接口和多态。
对classes而言接口是显示的,以函数签名为中心。多态是通过virtual函数发生于运行时期。
对templates而言接口是隐式的,基于有效表达式。多态是通过templates特化函数重载解析发生于编译时期。



了解typename的双重意义

声明模板时,前缀关键字classtypename可互换。
请使用关键字typename标识嵌套从属类型名称;但不得在基类列和成员初值列内以它作为基类修饰符。

当我们声明模板类型参数,classtypename意义完全相同。然而C++并不总是把classtypename视为等价。有时候你一定得使用typename
当使用模板时,如果需要访问某个依赖于模板参数的嵌套类型,必须在该类型前使用typename来明确指明这是一个类型。

template<typename T>
void Foo()
{
    typename T::SubType *ptr;//明确是SubType是一个类型而不是一个变量    
}

例外是不得在基类列和成员初值列内以它作为基类修饰符。

GPT:这些例外是因为在这些语境中,编译器已经能够清楚地识别出名称指向的是类型,不需要typename关键字来消除歧义。



学习处理模板化基类的名称

CRE:在派生类模板中访问基类模板的成员。这通常涉及到以下几种方法。

  1. 在基类函数调用动作之前加上this->
  2. 使用using声明式。
  3. 明白指出被调用的函数位于基类内。



将与参数无关的代码抽离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:

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)