[笔记]《Effective C++》第七章 Templates and Generic Programming

C++ template机制自身是一部完整的图灵机(Turing-complete):它可以被用来计算任何可计算的值。

条款41:Understand implicit interfaces and compile-time polymorphism.

  1. classes和templates都支持接口(interfaces)和多态(polymorphism)。
  2. classes而言接口是显式的(explicit),以函数签名为中心。多态则是通过virtual函数发生于运行期
  3. template参数而言,接口是隐式的(implicit),奠基于有效表达式。多态则是通过template具现化函数重载解析(function overloading resolution)发生于编译期
  • 在OOP(面向对象编程)中,总是以显式接口(explicit interfaces)和运行期多态(runtime polymorphism)解决问题。
  • 在Templates及泛型编程中,显式接口和运行期多态仍然存在,但重要性降低。反倒是隐式接口(implicit interfaces)和编译期多态(compile-time polymorphism)移到前头了。
    • 以不同的template参数具现化function templates”会导致调用不同的函数,这便是所谓的编译期多态(compile-timepolymorphism)。
    • 通常显式接口由函数的签名式(也就是函数名称参数类型返回类型)构成。
class Widget{
public:
  Widget();
  virtual ~Widget();
  virtual std::size_t size() const;
  virtual void normalize();
  void swap(Widget& other);
};
  • 隐式接口并不基于函数签名式,而是由有效表达式(valid expressions)组成。
template<typename T>
void doProcessing(T& w)
{
  if(w.size() > 10 && w != someNastyWidget)
  {
    T temp(w);
    temp.normalize();
    temp.swap(w);
  }
}
  • 加诸于template参数身上的隐式接口,就像加诸于class对象身上的显式接口一样真实,而且两者都在编译期完成检查

条款42:Understand the two meanings of typename.

  1. 声明template参数时,前缀关键字classtypename可互换。
  2. 请使用关键字typename标识嵌套从属类型名称;但不得在base class lists(基类列)member initialization list(成员初值列)内以它作为base class修饰符
  • template内出现的名称如果相依于某个template参数,称之为从属名称(dependent names)。如果从属名称在 class 内呈嵌套状,我们称它为嵌套从属名称(nested dependent name)。
  • C++有个规则可以解析(resolve)此一歧义状态:如果解析器在template中遭遇一个嵌套从属名称,它便假设这名称不是个类型,除非你告诉它是。
  • 任何时候当你想要在template中指涉一个嵌套从属类型名称,就必须在紧临它的前一个位置放上关键字 typename

条款43:Know how to access names in templatized base classes.

我们必须有某种办法令C++“不进入templatized base classes观察”的行为失效。

  • 第一是在base class函数调用动作之前加上"this->":
template< typename Company>
class LoggingMsgSender : public MsgSender<Company> {
public:
  ...
  void sendClearMsg(const MsgInfo& info)
  {
    //将“传送前”的信息写至log;
    this->sendClear(info);  //成立,假设sendClear将被继承。
    //将“传送后”的信息写至log;
  }
  ...
};
  • 第二是使用using声明式。
template< typename Company>
class LoggingMsgSender : public MsgSender<Company> {
public:
  using MsgSender<Company>::sendClear;  //告诉编译器,请它假设sendClear位于base class内。
  ...
  void sendClearMsg(const MsgInfo& info)
  {
    ...
    sendClear(info);  //成立,假设sendClear将被继承。
    ...
  }
  ...
};
  • 第三个做法是,明白指出被调用的函数位于baseclass内。但这往往是最不让人满意的一个解法,因为如果被调用的是virtual函数,上述的明确资格修饰(explicit qualification)会关闭“virtual绑定行为”
template< typename Company>
class LoggingMsgSender : public MsgSender<Company> {
public:
  ...
  void sendClearMsg(const MsgInfo& info)
  {
    ...
    MsgSender<Company>::sendClear(info);  //成立,假设sendClear将被继承。
    ...
  }
  ...
};

条款44:Factor parameter-independent code out of templates.

  1. Templates生成多个classes和多个函数,所以任何template代码不该与某个造成膨胀的template参数产生相依关系
  2. 非类型模板参数(non-type template parameters)而造成的代码膨胀,往往可消除,做法是以函数参数class成员变量替换template参数。
  3. 类型参数(type parameters)而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述(binary representations)的具现类型(instantiation types)共享实现码

条款45:Use member function templates to accept"all compatible types."

  1. 请使用member function templates(成员函数模板)生成“可接受所有兼容类型”的函数。
  2. 如果你声明member templates用于“泛化copy构造”或“泛化assignment操作”,你还是需要声明正常的copy构造函数copy assignment操作符
  • STL容器的迭代器几乎总是智能指针

条款46:Define non-member functions inside templates when type conversions are desired.

  • 当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template 内部的friend函数”。
  • 在template实参推导过程中从不将隐式类型转换函数纳入考虑。
  • template class内的friend声明式可以指涉某个特定函数。

条款47:Use traits classes for information about types.

  1. Traits classes使得“类型相关信息”在编译期可用。它们以templates和“templates特化”完成实现。
  2. 整合重载技术(overloading)后,traits classes有可能在编译期类型执行if...else测试。
  • STL迭代器分类:
    • Input迭代器:只能向前移动,一次一步,客户只可读取(不能涂写)它们所指的东西,而且只能读取一次
    • Output迭代器:只能向前移动,一次一步,客户只可涂写它们所指的东西,而且只能涂写一次
    • forward迭代器:可以做前述两种分类所能做的每一件事,而且可以读或写其所指物一次以上
    • Bidirectional迭代器:除了可以向前移动,还可以向后移动。
    • random access迭代器:可以在常量时间内向前或向后跳跃任意距离
    • 所有forward迭代器都是input迭代器,依此类推
  • 如何设计并实现一个traits class:
    • 确认若干你希望将来可取得的类型相关信息
    • 为该信息选择一个名称(例如iterator_category)。
    • 提供一个template和一组特化版本,内含你希望支持的类型相关信息
  • 如何使用一个traits class:
    • 建立一组重载函数(身份像劳工)或函数模板,彼此间的差异只在于各自的traits参数。令每个函数实现码与其接受之traits信息相应和。
    • 建立一个控制函数(身份像工头)或函数模板,它调用上述那些“劳工函数”并传递traits class所提供的信息。

条款48:Be aware of template metaprogramming.

  • Template metaprogramming(TMP,模板元编程)是编写template-based C++程序并执行于编译期的过程。
  • 所谓template metaprogram(模板元程序)是以C++写成执行于C++编译器内的程序。一旦TMP程序结束执行其输出,也就是从templates具现出来的若干C++源码,便会一如往常地被编译
  • TMP的优缺点:
    • 让某些事情更容易。
    • 可将工作从运行期转移到编译期。某些错误原本通常在运行期才能侦测到,现在可在编译期找出来。
    • 更高效:较小的可执行文件、较短的运行期、较少的内存需求
    • 编译时间变长
posted @   浮生的刹那  阅读(60)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
  1. 1 Night City r e l,Artemis Delta
  2. 2 Gold Steps(人生何处不青山) Neck Deep
  3. 3 Devil Trigger Ali Edwards
  4. 4 Hopeless Case Roam
  5. 5 On My Own Blitz Kids
  6. 6 I Really Want to Stay At Your House Rosa Walton & Hallie Coggins
  7. 7 Major Crimes Health & Window Weather
Night City - r e l,Artemis Delta
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.
点击右上角即可分享
微信分享提示