C++ 模板——学习笔记1

1.1 模板初探

1.1.1 模板定义

template<typename T>
T max (T a, T b)
{
	// 如果 b < a, 返回 a,否则返回 b
	return b < a ? a : b;
}

template<class T>
T max (T a, T b)
{
	// 如果 b < a, 返回 a,否则返回 b
	return b < a ? a : b;
}
其中typename或者class含义和功能一致。

1.1.2 模板使用

#include<iostream>
#include<time.h>
#include <string>
using namespace std;
template<class T>
T max (T a, T b)
{
	// 如果 b < a, 返回 a,否则返回 b
	return b < a ? a : b;
}
int main()
{
	int i = 42;
	std::cout << "max(7,i): " << ::max(7,i) << endl;
	double f1 = 3.4;
	double f2 = -6.7;
	std::cout << "max(f1,f2): " << ::max(f1,f2) << endl;
	std::string s1 = "mathematics";
	std::string s2 = "math";
	std::cout << "max(s1,s2): " << ::max(s1,s2) << endl;
}

其中max前面需要使用作用域限制符::,否则编译阶段会报错,加了::程序会在全局作用域中查找max()模板;并且书中提到,在编译阶段,模板并不是被编译成一个支持多种类型的实体,而是在用类型实例化模板时,都会产生一个独立的实体,按照上面这段代码,用int doulbe string实例化模板,会得到三个函数,下面通过readelf验证下,将cpp编译成.o后,通过readelf -s 查看内部的符号表,结果如下:

Symbol table '.symtab' contains 52 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS template.cpp
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT   10 
     6: 0000000000000000     1 OBJECT  LOCAL  DEFAULT   10 _ZStL19piecewise_construc
     7: 0000000000000000     1 OBJECT  LOCAL  DEFAULT    9 _ZStL8__ioinit
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT   11 
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT   12 
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT   13 
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT   14 
    12: 0000000000000000     0 SECTION LOCAL  DEFAULT   16 
    13: 00000000000002d7    73 FUNC    LOCAL  DEFAULT    6 _Z41__static_initializati
    14: 0000000000000320    21 FUNC    LOCAL  DEFAULT    6 _GLOBAL__sub_I_main
    15: 0000000000000000     0 SECTION LOCAL  DEFAULT   18 
    16: 0000000000000000     0 SECTION LOCAL  DEFAULT   20 
    17: 0000000000000000     0 SECTION LOCAL  DEFAULT   23 
    18: 0000000000000000     0 SECTION LOCAL  DEFAULT   24 
    19: 0000000000000000     0 SECTION LOCAL  DEFAULT   22 
    20: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
    21: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
    22: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
    23: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
    24: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
    25: 0000000000000000   727 FUNC    GLOBAL DEFAULT    6 main
    26: 0000000000000000     8 OBJECT  WEAK   HIDDEN    20 DW.ref.__gxx_personality_
    27: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZSt4cout
    28: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    29: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZStlsISt11char_traitsIcE
    30: 0000000000000000    28 FUNC    WEAK   DEFAULT   12 _Z3maxIiET_S0_S0_
    31: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSolsEi
    32: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZSt4endlIcSt11char_trait
    33: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSolsEPFRSoS_E
    34: 0000000000000000    40 FUNC    WEAK   DEFAULT   13 _Z3maxIdET_S0_S0_
    35: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSolsEd
    36: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSaIcEC1Ev
    37: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt7__cxx1112basic_stri
    38: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSaIcED1Ev
    39: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt7__cxx1112basic_stri
    40: 0000000000000000    74 FUNC    WEAK   DEFAULT   14 _Z3maxINSt7__cxx1112basic
    41: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZStlsIcSt11char_traitsIc
    42: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt7__cxx1112basic_stri
    43: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Unwind_Resume
    44: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __stack_chk_fail
    45: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __gxx_personality_v0
    46: 0000000000000000    40 FUNC    WEAK   DEFAULT   16 _ZStltIcSt11char_traitsIc
    47: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNKSt7__cxx1112basic_str
    48: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt8ios_base4InitC1Ev
    49: 0000000000000000     0 NOTYPE  GLOBAL HIDDEN   UND __dso_handle
    50: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt8ios_base4InitD1Ev
    51: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __cxa_atexit

这里看30 34 40行,就是之前提到过的int double string实例化为函数的结果,读者可以试着删除某行::max,然后通过readelf查看.o的符号表,会发现确实如书中所说,符号表会少 一行;
这里我有存在另外一个疑问了,通过函数模板指定类型实例化的结果与自己定义的函数在编译阶段有什么区别吗?
将代码改写下,新增一个函数string max (string a, string b),并且不通过函数模板去实例化string类型,得到的结果如下:

#include<iostream>
#include<time.h>
#include <string>
using namespace std;
string max (string a, string b)
{
	// 如果 b < a, 返回 a,否则返回 b
	return b < a ? a : b;
}

template<class T>
T max (T a, T b)
{
	// 如果 b < a, 返回 a,否则返回 b
	return b < a ? a : b;
}
int main()
{
	int i = 42;
	std::cout << "max(7,i): " << ::max(7,i) << endl;
	double f1 = 3.4;
	double f2 = -6.7;
	std::cout << "max(f1,f2): " << ::max(f1,f2) << endl;
	std::string s1 = "mathematics";
	std::string s2 = "math";
	std::cout << "max(s1,s2): " << ::max(s1,s2) << endl;
}
Symbol table '.symtab' contains 49 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS template.cpp
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 
     6: 0000000000000000     1 OBJECT  LOCAL  DEFAULT    9 _ZStL19piecewise_construc
     7: 0000000000000000     1 OBJECT  LOCAL  DEFAULT    8 _ZStL8__ioinit
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT   10 
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT   11 
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT   13 
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT   14 
    12: 00000000000001f9    73 FUNC    LOCAL  DEFAULT    5 _Z41__static_initializati
    13: 0000000000000242    21 FUNC    LOCAL  DEFAULT    5 _GLOBAL__sub_I__Z3maxNSt7
    14: 0000000000000000     0 SECTION LOCAL  DEFAULT   15 
    15: 0000000000000000     0 SECTION LOCAL  DEFAULT   17 
    16: 0000000000000000     0 SECTION LOCAL  DEFAULT   20 
    17: 0000000000000000     0 SECTION LOCAL  DEFAULT   21 
    18: 0000000000000000     0 SECTION LOCAL  DEFAULT   19 
    19: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
    20: 0000000000000000     0 SECTION LOCAL  DEFAULT    2 
    21: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
    22: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
    23: 0000000000000000    74 FUNC    GLOBAL DEFAULT    5 _Z3maxNSt7__cxx1112basic_
    24: 0000000000000000    40 FUNC    WEAK   DEFAULT   11 _ZStltIcSt11char_traitsIc
    25: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    26: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt7__cxx1112basic_stri
    27: 000000000000004a   431 FUNC    GLOBAL DEFAULT    5 main
    28: 0000000000000000     8 OBJECT  WEAK   HIDDEN    17 DW.ref.__gxx_personality_
    29: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZSt4cout
    30: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZStlsISt11char_traitsIcE
    31: 0000000000000000    28 FUNC    WEAK   DEFAULT   13 _Z3maxIiET_S0_S0_
    32: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSolsEi
    33: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZSt4endlIcSt11char_trait
    34: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSolsEPFRSoS_E
    35: 0000000000000000    40 FUNC    WEAK   DEFAULT   14 _Z3maxIdET_S0_S0_
    36: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSolsEd
    37: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSaIcEC1Ev
    38: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt7__cxx1112basic_stri
    39: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSaIcED1Ev
    40: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt7__cxx1112basic_stri
    41: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Unwind_Resume
    42: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __stack_chk_fail
    43: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __gxx_personality_v0
    44: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNKSt7__cxx1112basic_str
    45: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt8ios_base4InitC1Ev
    46: 0000000000000000     0 NOTYPE  GLOBAL HIDDEN   UND __dso_handle
    47: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _ZNSt8ios_base4InitD1Ev
    48: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __cxa_atexit

见23行 31行 35行,23行是非模板函数string max (string a, string b),31和35行分别是函数模板实例化出来的int类型模板函数以及double类型的模板函数,唯一的区别是bind那列,23行是GLOBAL,31行和35行是WEAK;这列的解释如下:
Bind = GLOBAL binding means the symbol is visible outside the file. LOCAL binding is visible only in the file. WEAK is like global, the symbol can be overridden.
如字面意思,WEAK的函数符号可被覆盖,看起来有些像是C语言中强符号和弱符号的意思,所以从另一方面印证了上面为什么在链接阶段不会报符号重复定义,因为string max (string a, string b)
是强符号,会优先使用这里的函数定义而不会去使用函数模板实例化string的函数,这里我本地做了验证,发现把自己定义的函数b<a改成b>a,::max会使用自己定义的函数而不是函数模板的函数。

1.1.3 两阶段编译检查

概括来说,就是
模板的检查第一步是先检查不包含类型参数的检查:
1.语法检查。比如少了分号。
2.使用了未定义的不依赖于模板参数的名称(类型名,函数名,…)。
3.未使用模板参数的 static assertions。
第二步在实例化阶段,检查依赖于类型参数的部分;书中还提到一点,如果模板没有被实例化的话,有些编译器并不会执行第一阶段在的所有检查,导致一些代码的常规错误发现不了。

1.1.4 编译和链接

两阶段的编译检查给模板的处理带来了一个问题:当实例化一个模板的时候,编译器需要(一
定程度上)看到模板的完整定义。这不同于函数编译和链接分离的思想,函数在编译阶段只
需要声明就够了。第 9 章将讨论如何应对这一问题。我们将暂时采取最简单的方法:将模板
的实现写在头文件里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值