模板类成员函数的定义和声明为什么要放在一个文件中

“通常情况下,你会在.h文件中声明函数和类,而将它们的定义放置在一个单独的.cpp文件中。但是在使用模板时,这种习惯性做法将变得不再有用,因为当实例化一个模板时,编译器必须看到模板确切的定义,而不仅仅是它的声明。因此,最好的办法就是将模板的声明和定义都放置在同一个.h文件中。这就是为什么所有的STL头文件都包含模板定义的原因。”[1]

"标准要求编译器在实例化模板时必须在上下文中可以查看到其定义实体;而反过来,在看到实例化模板之前,编译器对模板的定义体是不处理的——原因很简单,编译器怎么会预先知道 typename 实参是什么呢?因此模板的实例化与定义体必须放到同一翻译单元中。"[1]

"《C++编程思想》第15章(第300页)说明了原因:

模板定义很特殊。由template<…> 处理的任何东西都意味着编译器在当时不为它分配存储空间,它一直处于等待状态直到被一个模板实例告知。在编译器和连接器的某一处,有一机制能去掉指定模板的多重定义。所以为了容易使用,几乎总是在头文件中放置全部的模板声明和定义。"[2]

"对C++编译器而言,当调用函数的时候,编译器只需要看到函数的声明。当定义类类型的对象时,编译器只需要知道类的定义,而不需要知道类的实现代码。因此,因该将类的定义和函数声明放在头文件中,而普通函数和类成员函数的定义放在源文件中。

但在处理模板函数和类模板时,问题发生了变化。要进行实例化模板函数和类模板,要求编译器在实例化模板时必须在上下文中可以查看到其定义实体;而反过来,在看到实例化模板之前,编译器对模板的定义体是不处理的——原因很简单,编译器怎么会预先知道 typename 实参是什么呢?因此模板的实例化与定义体必须放到同一翻译单元中。"[3]

[1] 任何时候都适用的20个C++技巧. http://www.uml.org.cn/c++/20112284.asp 

[2] 为什么不能将类模板的声明与类模板函数实现分开写. http://blog.sina.com.cn/s/blog_684355870100jmjr.html

[3]类模板和模板函数连接出错处理. http://www.cppblog.com/kenny/archive/2011/04/23/144841.html

[4]知乎上有一个能实现分开编译的例子,但是很不人性化。。。http://www.zhihu.com/question/20630104

模板类成员函数的定义和声明为什么要放在一个文件中

时间: 2024-11-10 15:25:16

模板类成员函数的定义和声明为什么要放在一个文件中的相关文章

将类的定义放在头文件中,把成员函数的实现代码放在一个cpp文件中

写这种.h和.cpp文件分开的大程序,虽然对很多人来说很简单,对自己来说算是第一次吧,好好学C++,加油~ 题目:定义Point类,由Point派生出Circle类,再由Circle派生出Cylinder类.将类的定义部分分别作为3个头文件,对他们的成员函数的定义分别作为3个源文件 1.Point.h文件 1 #ifndef POINT_H 2 #define POINT_H 3 #include<iostream> //头文件也需要包含这个 4 using namespace std; 5

C++基础知识(六)--类--成员函数的定义--对象的创建与使用--从面向过程到面向对象

一.类 1.类是一种数据类型,将数据与对数据的操作(函数)放到一起.一个类中的数据通常只能通过本类提供的方法进行处理,这些方法成为该类与外部的接口,对象之间通过消息进行通讯. 2.如果在类的起始点无访问说明符,系统默认为私有(private) 3.类是一种数据类型,定义时系统不为类分配存储空间,所以不能对类的数据成员进行初始化.类中的任何数据成员也不能使用关键字extern,auto,register等关键字限定其存储类型 二.成员函数的定义 1.函数定义:通常在类定义中,成员函数仅做声明,函数

实现类成员函数回调

一.采用tri::function/bind方法实现类成员函数内部调用 首先声明函数类型 1 std::tr1::function<double(double)> func; 在需要调用函数的地方绑定函数 1 switch (flgFun) { 2 case flgSine: 3 func = std::tr1::bind(&CAnalog::Sin, this, std::tr1::placeholders::_1); 4 break; 5 case flgSquare: 6 fun

C++类成员函数

c++的两大特色是多态和模板.其中多态是通过继承和虚函数来实现的,其中虚函数是通过每个对象里面的虚表来实现的.如果这个对象的类有虚函数,那么这个类就有一张虚表,存的是每个虚函数的入口地址,而这个类的每个对象,都会有一个4字节的指针,指向这张虚表,这个就是虚指针. 上面一段话很多人都知道,但是如果问普通成员函数,编译器是怎么找到它的入口地址的呢?也就是说,怎么进行调用?为什么A类一个foo函数和B类一个foo函数,A类的对象.foo就一定是调用A的foo?有人会说运行时类型识别RTTI.假如识别出

让类成员函数指针成为可调用对象

类成员函数指针实践上是一个指针类型,不可直接通过调用运算符()作为可调用对象调用,一般调用该类成员函数指针需要指定该指针对应的对象. 一般情况下调用类成员函数指针: // a.h #ifndef A_H #define A_H #include <iostream> using std::cout; using std::endl; class A{ public:     void print(); }; #endif // a.cpp #include "a.h" vo

C++ 获取类成员函数地址方法 浅析

C语言中可以用函数地址直接调用函数: void print () { printf ("function print"); } typdef void (*fun)(); fun f = print; f(); C++中类非静态成员函数必须通过实例去调用,C++中类成员函数调用: class test { public: void print () { printf ("function print"); } }; 我们同样可以通过定义函数指针来调用如下: type

C++的const类成员函数

转自:http://blog.csdn.net/lihao21/article/details/8634876 我们知道,在C++中,若一个变量声明为const类型,则试图修改该变量的值的操作都被视编译错误.例如, [cpp] view plain copy const char blank = ''; blank = '\n';  // 错误 面向对象程序设计中,为了体现封装性,通常不允许直接修改类对象的数据成员.若要修改类对象,应调用公有成员函数来完成.为了保证const对象的常量性,编译器

【转】C++的const类成员函数

我们知道,在C++中,若一个变量声明为const类型,则试图修改该变量的值的操作都被视编译错误.例如, const char blank=' '; blank='\n'; //错误 面向对象程序设计中,为了体现封装性,通常不允许直接修改类对象的数据成员.若要修改类对象,应调用公有成员函数来完成.为了保证const对象的常量性,编译器须区分不安全与安全的成员函数(即区分试图修改类对象与不修改类对象的函数).例如, const Screen blankScreen;  //Screen为class,

直接调用类成员函数地址(用汇编取类成员函数的地址,各VS版本还有所不同)

在C++中,成员函数的指针是个比较特殊的东西.对普通的函数指针来说,可以视为一个地址,在需要的时候可以任意转换并直接调用.但对成员函数来说,常规类型转换是通不过编译的,调用的时候也必须采用特殊的语法.C++专门为成员指针准备了三个运算符: "::*"用于指针的声明,而"->*"和".*"用来调用指针指向的函数. // Thunk.cpp : Defines the entry point for the console applicatio