转:《C++ Templates》读书笔记

2023-11-10

有三种模板参数(形参):

(1)类型参数(这是使用得最多的)
(2)非类型参数
(3)模板的模板参数
类型参数:

  类型参数是通过关键字typename或者class引入。关键字后面必须是一个简单的标识符,后面用逗号来隔开下一个参数声明,等号代表接下来的是缺省模板实参,一个封闭的尖括号(>)表示参数化子句的结束。

  在模板声明内部,类型参数的作用类似于typedef名称。例如,如果T是一个模板参数,就不能使用诸如class T等形式的修饰名称,即使T是一个要被class类型替换的参数也不可以。 

template <typename Allocator>
class List {
      class Allocator* allocator;             // Error
      friend class Allocator;                   // Error
      …
};
非类型参数:

   非类型参数表示的是:在编译期或链接期可以确定的常值(模板的模板参数也不属于类型模板参数,但讨论非类型模板参数时,并不考虑模板的模板参数)。这种参数的类型必须是下面的一种:

       1. 整型或者枚举类型
       2. 指针类型
(包含普通对象的指针类型、函数指针类型、指向成员的指针类型)
       3. 引用类型(指向对象或者指向函数的引用都是允许的)

  所有其他的类型现今都不允许作为非类型参数使用(但在将来很可能会增加浮点数类型)。在某些情况下,非模板参数的声明也可以使用关键字typename

template <typename T, typename T::Allocator* Allocator>
class List;

  这两个参数的区别在于:第1个typename后面是一个简单标识符T,而第2个typename后面是一个受限的名称(即是一个包含双冒号 :: 的名称),它表示Allocator是属于T的一个子类型。

   函数和数组类型也可以被指定为非模板参数,但要把它们先隐式地转换为指针类型,这种转型也成为decay

template <int buf[5]> class Lexer;               // buf实际上是一个int* 类型
template <int* buf> class Lexer;                  // 这是上面的重新声明

  非类型模板参数的声明和变量的声明很相似,但它们不能具有staticmutable等修饰符;只能具有constvolatile[1]限定符。但如果这两个限定符限定的如果是最外层的参数类型,编译器将会忽略它们:

template <int const length> class Buffer;           //  const在这里是没用的,被忽略了
tempalte <int length> class Buffer;                    // 和上面等价

  最后,非类型模板参数只能是右值:它们不能被取址,也不能被赋值。

模板的模板参数:

    模板的模板参数是代表类模板的占位符。它的声明和类模板的声明很类似,但不能使用关键字structunion

 template <template<typename X> class C>     //正确
 void f(C<int>* p);

 template <template<typename X> struct C>     //错误
 void f(C<int>* p);

 template <template<typename X> union C>     //错误
 void f(C<int>* p);

  在它们声明的作用域中,模板的模板参数的用法和类模板的用法很类似。模板的模板参数的参数(如下面的A)可以具有缺省模板实参。显然,只有在调用时没有指定该参数的情况下,才会应用缺省模板实参: 

template <template<typename T, typename A = MyAllocator> class Container>
class Adaptation {
       Container<int> storage;           // 隐式等同于 Container<int, MyAllocator>
       …
};
  对于模板的模板参数而言,它的参数名称只能被自身其他参数的声明使用。下面的假设例子说明了这一点:
template <template<typename T, T*> class Buf>
class Lexer {
      static char storage[5];
      Buf<char, &Lexer<Buf>::storage[0]> buf;
      …
};
template <template<typename T> class List>
class Node {
      static T* storage;           // 错误:模板的模板参数的参数在这里不能被使用
      …
};
   通常而言,模板的模板参数的参数名称(如上面的例子)并不会在后面被用到。因此,该参数也经常被省略不写,即没有命名。例如,前面的 Adaptation模板的例子可以这样声明:
template <template<typename, typename = MyAllocator> class Container>
 class Adaptation {
       Container<int> storage;          // 隐式等同于 Container<int, MyAllocator>
       …
};
缺省模板实参:

    现今,只有类模板声明才能具有缺省模板实参。任何类型的模板参数都可以拥有一个缺省实参,只要该缺省实参能够匹配这个参数就可以。显然,缺省实参不能依赖于自身的参数;但可以依赖于前面的参数:

template <typename T, typename Allocator = allocator<T> >
class List;              // 就是说,allocator<T>不能依赖于本身参数Allocator,但是能依赖于前面参数T

       与缺省的函数调用参数的约束一样,对于任一个模板参数,只有在之后的模板参数都提供了缺省实参的前提下,才能具有缺省模板实参。后面的缺省值通常是在同个模板声明中提供的,但也可以在前面的模板声明中提供。下面的例子说明了这一点:

template <typename T1, typname T2, typename T3,
                       typename T4 = char, typename T5 = char>
class Quintuple;                        // 正确

template <typename T1, typname T2, typename T3 = char,
                       typename T4, typename T5>
class Quintuple;                        // 正确,根据前面的模板声明,T4和T5已经具有缺省值了

template <typename T1 = char, typname T2, typename T3,
                       typename T4, typename T5>
class Quintuple;                        // 错误,T1不能具有缺省实参,因为T2还没有缺省实参
       另外,缺省实参不能重复声明:
template <typename T = void>
class Value;
template <typename T = void>
class Value;                               // 错误,重复出现的缺省实参
----------------------------------------------------------------------------------------------------------------------------------

[1] 关于volatile,它的作用在于限定一个对象可以被外部进程(操作系统、硬件或并发进程等)改变,声明的语法如下:    
   int volatile nVint;

参考:http://hi.baidu.com/b1ghost/blog/item/daf6b7445f10392fcffca392.html

 

《C++ Templates》读书笔记(二):模板实参
2009-08-17 17:45

模板实参是指:在实例化模板时,用来替换模板参数的值。我们可以使用下面几种不同的机制来确定这些值。

类型实参:

       模板的类型实参是一些用来指定模板类型参数的值。我们平时使用的大多数类型都可以被用作模板的类型实参,但有两种情况例外:

       1. 局部类和局部枚举(换句话说,指在函数定义内部声明的类型)不能作为模板的类型实参。
       2. 未命名的class类型或者未命名的枚举类型不能作为模板的类型实参(然而,通过typedef声明给出的未命名类和枚举是可以作为模板类型实参的)。

       下面的例子很好地说明了这两种例外情况:

       template <typename T> class List {
             …
       };

       typedef struct {
              double x, y, z;
       } Point;

       typedef enum { red, green, blue } *ColorPtr;

       int main()
       {
              struct Association                             // 局部类型
              {
                      int* p;
                       int* q;
              };
              List<Association*> error1;                // 错误:模板实参中使用了局部类型
              List<ColorPtr> error2;                      // 错误:模板实参中使用了未命名的类型,因为typedef定义
                                                                       // 的是*ColorPtr,不是ColorPtr
              List<Point> ok;                                 // 正确:通过使用typedef定义的未命名类型
       }

   通常而言,尽管其他的类型都可以用作模板实参,但前提是该类型替换模板参数之后获得的构造必须是有效的。
tempalte <typename T>
void clear (T p)
{
       *p = 0;                // 要求单目运算符*可以用于类型T
}
int main()
{
      int a;
      clear(a);               // 错误:int类型并不支持但不运算符*
}
非类型实参:

       非类型模板实参是那些替换非类型参数的值。这个值必须是以下几种中的一种:

       1. 某一个具有正确类型的非类型模板参数
       2. 一个编译期整型常数(或枚举值)。这只有在参数类型和值的类型能够进行匹配,或者值的类型可以隐式地转换为参数类型(例如,一个char值可以作为int参数的实参)的前提下,才是合法的。
       3. 前面有单目运算符&(即取址)的外部变量或者函数的名称。对于函数或数据变量,&运算符可以省略。这类模板实参可以匹配指针类型的非类型参数。
       4. 对于引用类型的非类型模板参数,前面没有&运算符的外部变量和外部函数也是可取的。
       5. 一个指向成员的指针常量。换句话说,类似&C::m的表达式,其中C是一个class类型,m是一个非静态成员(成员变量或者函数)。这类实参只能匹配类型为“成员指针”的非类型参数。

       当实参匹配“指针类型或者引用类型的参数”时,用户定义的类型转换(例如单参数的构造函数和重载类型转换运算符)和由派生类到基类的类型转换,都是不会被 考虑的;即使在其他情况下,这些隐式类型转换时有效的,但在这里都是无效的。隐式类型转换的唯一应用只能是:给实参加上关键字const或者volatile。

       下面是一些有效的非类型模板实参的例子:

tempalte <typename T, T nontype_parm>
class C;
C<int, 33>* c1;               // 整型
int a;
C<int*, &a>* c2;              // 外部变量地址
void f();
void f(int);
C<void(*)(int), f>* c3;       // 函数名称。在这个例子中,重载解析会选择f(int),f前面的&隐式省略了
class X {
 public:
        int n;
        static bool b;
};
C<bool&, X::b>* c4;        // 静态类成员是可取的变量(和函数)名称
       C<int X::*, &X::n>* c5;      // 指向成员的指针常量
       template <typename T>
       void templ_func();
 C<void(), &templ_func<double> >* c6;        // 函数模板实例同时也是函数
    模板实参的一个普遍约束是:在程序创建的时候,编译器或者链接器要能够确定实参的值。如果实参的值要等到程序运行时才能够确定(譬如,局部变量的地址),就不符合“模板是在程序创建的时候进行实例化”的概念了。

       另一方面,有些常值不能作为有效的非类型实参,这也许会令人觉得很诧异。这些常值包括:

       · 空指针常量
       · 浮点型值
       · 字符串

       有关字符串的一个问题就是:两个完全等同的字符串可以存储在两个不同的地址中。在此,我们用一种(很笨的)解决方法来表达需要基于字符串进行实例化的模板:引入一个额外的变量来存储这个字符串。

template <char const* str>
class Message;
extern char const hello[] = "Hello World!";
Message<hello>* hello_msg;

 可以看到,我们使用了关键字extern。因为如果不使用这个关键字,上面的const数组变量具有内部链接。下面给出一些错误的例子:

template <typename T, T nontype_parm>
class C;
class Base {
      public:
             int i;
} base;
class Derived: public Base {
} derived_obj;
C<Base*, &derived_obj>* err1;       // 错误,这里不会考虑派生类到基类的类型转换
C<int&, base.i>* err2;                     // 错误,域运算符(.)后面的变量不会被看成变量
int a[10];
 C<int*, &a[0]>* err3;                       // 错误,单一数组元素的地址并不是可取的

模板的模板实参:

   “模板的模板实参”必须是一个类模板,它本身具有参数,该单数必须精确匹配它“所替换的模板的模板参数”本身的参数。在匹配过程中,“模板的模板实参”的 缺省模板实参将不会考虑(但是如果“模板的模板参数”具有缺省实参,那么模板的实例化过程是会考虑模板的模板参数的缺省实参的)。 
       这段看起来很绕,看下面例子能更易理解。

#include <list>
       // List 的声明:
       //      namespace std {
       //             template <typename T, typename Allocator = allocator<T> >
        //             class list;
       //       }

 tempalte <typename T1, typename T2,
                             tempalte<typename> class Container>
                                                     // Container期望的是只具有一个参数的模板
 class Relation {
    public:
         …
    private:
         Container<T1> dom1;
         Container<T2> dom2;
 };
 int main()
 {
         Relation<int, double, std::list> rel;    // 错误,std::list是一个具有两个参数的模板
         …
  }
   这里的问题是:标准库中的 std::list模板具有两个参数,它的第2个参数(我们称之为内存配置器 allocator)具有一个缺省值;但当我们匹配 std::listContainer参数时,事实上并不会考虑这个缺省值(即认为缺省值并不存在)。

  有时,我们可以通过给模板的模板参数添加一个具有缺省值的参数,来解决这个问题。在前面的例子中,我们可以这样改写Relation模板: 

#include <memory>
tempalte <typename T1, typename T2,
                             tempalte<typename T, typename = std::allocator<T> > class Container>
                                              // Container现在就能够接受一个标准容器模板了
class Relation {
    public:
         …
    private:
         Container<T1> dom1;
         Container<T2> dom2;
 };
  显然,这并不是一个令人满意的解决方案,但它可以让标准容器模板得到使用。另外我们注意到了一个事实:从语法上讲,只有关键字 class才能被用来声明模板的模板参数;但是这并不意味着只有用关键字 class声明的类模板才能作为它的替换实参。实际上,“ struct模板”、“ union模板”都可以作为模板的模板参数的有效实参。这和我们前面所提到的事实很相似:对于用关键字 class声明的模板类型参数,我们可以用(满足约束的)任何类型作为它的替换实参。

 

参考:http://hi.baidu.com/b1ghost/blog/item/daf6b74458043e2fcffca38e.html 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

转:《C++ Templates》读书笔记 的相关文章

  • 如何验证文件名称在 Windows 中是否有效?

    是否有一个 Windows API 函数可以将字符串值传递给该函数 该函数将返回一个指示文件名是否有效的值 我需要验证文件名是否有效 并且我正在寻找一种简单的方法来完成此操作 而无需重新发明轮子 我正在直接使用 C 但针对的是 Win32
  • ASP.NET Core Serilog 未将属性推送到其自定义列

    我有这个设置appsettings json对于我的 Serilog 安装 Serilog MinimumLevel Information Enrich LogUserName Override Microsoft Critical Wr
  • 当我使用“control-c”关闭发送对等方的套接字时,为什么接收对等方的套接字不断接收“”

    我是套接字编程的新手 我知道使用 control c 关闭套接字是一个坏习惯 但是为什么在我使用 control c 关闭发送进程后 接收方上的套接字不断接收 在 control c 退出进程后 发送方的套接字不应该关闭吗 谢谢 我知道使用
  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • 如何在列表框项目之间画一条线

    我希望能够用水平线分隔列表框中的每个项目 这只是我用于绘制项目的一些代码 private void symptomsList DrawItem object sender System Windows Forms DrawItemEvent
  • C++ 子字符串返回错误结果

    我有这个字符串 std string date 20121020 我正在做 std cout lt lt Date lt lt date lt lt n std cout lt lt Year lt lt date substr 0 4 l
  • 使闭包捕获的变量变得易失性

    闭包捕获的变量如何与不同线程交互 在下面的示例代码中 我想将totalEvents 声明为易失性的 但C 不允许这样做 是的 我知道这是错误的代码 这只是一个例子 private void WaitFor10Events volatile
  • WPF TabControl,用C#代码更改TabItem的背景颜色

    嗨 我认为这是一个初学者的问题 我搜索了所有相关问题 但所有这些都由 xaml 回答 但是 我需要的是后台代码 我有一个 TabControl 我需要设置其项目的背景颜色 我需要在选择 取消选择和悬停时为项目设置不同的颜色 非常感谢你的帮助
  • 在 ASP.NET Core 3.1 中使用包含“System.Web.HttpContext”的旧项目

    我们有一些用 Net Framework编写的遗留项目 应该由由ASP NET Core3 1编写的API项目使用 问题是这些遗留项目正在使用 System Web HttpContext 您知道它不再存在于 net core 中 现在我们
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • 从路径中获取文件夹名称

    我有一些路c server folderName1 another name something another folder 我如何从那里提取最后一个文件夹名称 我尝试了几件事 但没有成功 我只是不想寻找最后的 然后就去休息了 Thank
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • 将 unsigned char * (uint8_t *) 转换为 const char *

    我有一个带有 uint8 t 参数的函数 uint8 t ihex decode uint8 t in size t len uint8 t out uint8 t i hn ln for i 0 i lt len i 2 hn in i
  • 插入记录后如何从SQL Server获取Identity值

    我在数据库中添加一条记录identity价值 我想在插入后获取身份值 我不想通过存储过程来做到这一点 这是我的代码 SQLString INSERT INTO myTable SQLString Cal1 Cal2 Cal3 Cal4 SQ
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • C - 直接从键盘缓冲区读取

    这是C语言中的一个问题 如何直接读取键盘缓冲区中的数据 我想直接访问数据并将其存储在变量中 变量应该是什么数据类型 我需要它用于我们研究所目前正在开发的操作系统 它被称为 ICS OS 我不太清楚具体细节 它在 x86 32 位机器上运行
  • 为什么我收到“找不到编译动态表达式所需的一种或多种类型。”?

    我有一个已更新的项目 NET 3 5 MVC v2 到 NET 4 0 MVC v3 当我尝试使用或设置时编译出现错误 ViewBag Title财产 找不到编译动态表达式所需的一种或多种类型 您是否缺少对 Microsoft CSharp
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • 使用 libcurl 检查 SFTP 站点上是否存在文件

    我使用 C 和 libcurl 进行 SFTP FTPS 传输 在上传文件之前 我需要检查文件是否存在而不实际下载它 如果该文件不存在 我会遇到以下问题 set up curlhandle for the public private ke
  • 使用按位运算符相乘

    我想知道如何使用按位运算符将一系列二进制位相乘 但是 我有兴趣这样做来查找二进制值的十进制小数值 这是我正在尝试做的一个例子 假设 1010010 我想使用每个单独的位 以便将其计算为 1 2 1 0 2 2 1 2 3 0 2 4 虽然我

随机推荐

  • 程序员搜索引擎比较

    正如大家口中所说的一样 百度搜索引擎吃相太难看了 如果谷歌搜索还在的话 百度搜索的日子可能并不会太好过 其他搜索引擎 1 360搜索 比较少用甚至没有用过的搜索引擎 360的全家桶太恶心了 简直是流氓 所以很多时候360安全管家 杀毒什么的
  • 开源框架 WebFirst 一键生成项目,在线建表

    1 WebFirst框架描述 WebFirst 是果糖大数据团队开发的新一代 高性能 代码生成器 数据库设计工具 由 net core sqlsugar 开发 导入1000个表只要1 2秒 用法简单 功能强大 支持多种数据库 具体功能如下
  • Bundle Adjustment 光束平差法

    https www cnblogs com Jessica jie p 7739775 html 感觉这个链接讲的比较好理解 看slam的书完全一脸懵
  • linux启动redis失败,解决redis服务启动失败的问题

    最近学redis 就遇到了各种坑 在这里分享一下 我是将redis做成后台 安装 配置环境变量统统省略掉了 做成后台服务呢 首先 cd到redis的安装目录下 再cd到util 接着执行 install server sh 然后修改服务名称
  • windows批处理文件删除n天前的文件

    author skate time 2010 12 23 windows批处理文件删除n天前的文件 用批处理文件删除n天前的文件如果操作系统是 Windows Server 2003 那就好办了 因为它有一个forfiles命令能够查找满足
  • BI大数据名词术语

    大数据的出现带来了许多新的术语 但这些术语往往比较难以理解 因此 我们通过本文给出一个常用的大数据术语表 抛砖引玉 供大家深入了解 部分定义参考了相应的博客文章 A 聚合 Aggregation 搜索 合并 显示数据的过程 算法 Algor
  • Postman之全局变量、环境变量

    1 什么是环境变量 环境变量 postman可以自定义环境参数值 这样就不用每次请求都去输入某些值 直接引用设置的值 使我们的测试更方便 例如 通过变换环境变量来快速变换环境地址 可将本地 测试环境的IP添加至不同的环境变量 调用时使用 x
  • 'react-scripts' 不是内部或外部命令 、propTypes is not defined 解决办法

    在手动安装了React 提供的第三方库 prop types报错的 我的React 就报错运行不了了 报错如下 react scripts 不是内部或外部命令 也不是可运行的程序 或批处理文件 这是一个在学习React 踩的坑 百度了一下
  • webpack打包vue

    在Windows10系统下 自定义打包整个Vue文件夹项目的相关配置项涉及以下内容 安装Node js和npm Node js官网 https nodejs org en download npm是Node js的包管理工具 在Node j
  • JDK8 Date 日期常用使用方法

    JDBC日期的转换 最新JDBC映射将把数据库的日期类型和Java 8的新类型关联起来 SQL gt Java date gt LocalDate time gt LocalTime timestamp gt LocalDateTime L
  • X11相关参数设置

    X11相关参数设置 X11 环境变量 DISPLAY 用来设置将图形显示到何处 变量格式 Xlib connection to 0 0 refused by server 开关闭屏幕显示 连接实际屏幕 确认实际屏幕的name of disp
  • 模式识别和计算机应用(转载)

    导读 只是覆盖了很小的范围 但总结的不错 1 数学方面 1 矩阵的各种分解 比如 LU QR Cholesky SVD Polar 2 广义逆与子空间 3 最小二乘法 特别齐性方程Ax b的各种解法及其几何意义 4 凸分析与凸优化的基本知识
  • 8种专坑同事的 SQL 写法,性能降低100倍,你学会了吗?

    程序员的成长之路 互联网 程序员 技术 资料共享 关注 阅读本文大概需要 14 分钟 来自 juejin cn post 6844903998974099470 1 LIMIT 语句 分页查询是最常用的场景之一 但也通常也是最容易出问题的地
  • R语言Logistic回归模型深度验证以及Nomogram绘制

    R语言Logistic回归模型深度验证以及Nomogram绘制 小易学统计 互联网医疗统计师 自立 爱家人 15 人 赞同了该文章 01 研究背景
  • openwrt解除软件包安装限制

    Openwrt解除验证限制 sed i s tdetect package t detect package g koolshare scripts ks tar install sh
  • 安卓开发笔记——关于java.lang.RuntimeException: Unable to start activity ComponentInfo{......}问题的解决方案

    笔者在实现一个消息上下轮播时 遇到APP闪退 将日志打印出来 Process com ghl intelligence PID 6557 java lang RuntimeException Unable to start activity
  • 公众号如何快速接入查题功能

    最近很多同学私信小睿 我不会编程 写代码 我怎么能做一个自己的查题公众号呢 既然这么多同学最近在问 那小睿今天就来告诉大家 调用量瞩目 首先你需要一个查题的接口 然后配置到公众号内就可以了 那么接口从哪儿来呢 当然是找小睿啦 小睿为了使大家
  • 内核自带的基于GPIO的LED驱动学习(二)

    2 分析平台驱动的probe函数 好 既然这个LED驱动使用的是平台驱动框架 当设备和驱动匹配上之后 就会执行指定的probe函数 那接下来的工作就转移到分析对应的probe函数了 为了直观 我把probe函数也粘贴上来 static in
  • 【计算机网络】湖科大微课堂笔记 p2-p3 因特网概述、三种交换方式

    视频 计算机网络已从通信基础设施发展成重要的信息服务基础设施 因特网概述 网络 互联网和因特网的概念 理解 因特网发展的三个阶段 了解 因特网的标准化工作 了解 因特网的组成 理解 网络 互联网和因特网的概念 理解 左图是网络 右图是互联网
  • 转:《C++ Templates》读书笔记

    有三种模板参数 形参 1 类型参数 这是使用得最多的 2 非类型参数 3 模板的模板参数 类型参数 类型参数是通过关键字typename或者class引入 关键字后面必须是一个简单的标识符 后面用逗号来隔开下一个参数声明 等号代表接下来的是