谈谈C++中模板分离式编译出现的一些问题

什么是分离式编译

通俗的来讲就是将声明和定义分离在不同文件中

一个程序由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有
目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。


正常函数与模板分离式编译

看代码,先区分两个

正常函数的分离式编译

//------------------------ Test.h 存放函数的声明-----------------------
#pragma once
#include "iostream"
using namespace std;
//正常函数的声明
int Add(const int& num1, const int& num2);
//------------------------ Test.h-----------------------

//------------------------ Test.cpp  存放函数的定义 -----------------------
#include"Test.h"

int Add(const int& num1, const int& num2)
{
	cout << "int Add(const int& num1, const int& num2)" << endl;
	return 0;
}
//------------------------ Test.cpp -----------------------

//------------------------ main.cpp -----------------------
#include"Test.h"

int main()
{
	Add(1, 1);
	return 0;
}
//------------------------ main.cpp -----------------------

在这里插入图片描述

模板的分离式编译

//------------------------ Test.h 存放声明-----------------------
#pragma once
#include "iostream"
using namespace std;

//函数模板的声明
template <class T>
T Add(const T& num1, const T& num2);
//------------------------ Test.h-----------------------

//------------------------ Test.cpp  存放定义 -----------------------
#include"Test.h"

template<class T>
T Add(const T& num1, const T& num2)
{
	cout << "T Add(const T& num1, const T& num2)" << endl;
}
//------------------------ Test.cpp -----------------------

//------------------------ main.cpp -----------------------
#include"Test.h"

int main()
{
	Add(1, 1);
	return 0;
}
//------------------------ main.cpp -----------------------

在这里插入图片描述
此时我们看到出现了一个链接错误,那么问题来了:为什么模板的分离式编译会出现错误?为什么会出现这个错误?怎么解决模板分离式编译的问题呢?


为什么模板分离式编译会出错?

C++Primer中这么说:当使用一个vector这样的泛型类型,或者find这样的泛型函数时,我们提供足够的信息,将模板转化为特定的类或者函数(实例化)。这样的转换发生在编译阶段
我们知道了模板的实例化发生在编译阶段

回顾编译过程
  • 预处理阶段
    头文件展开、条件编译指令、宏替换
  • 编译阶段
    将第一步产生的文件同其他文件一起编译成汇编代码
  • 汇编阶段
    将汇编源码转换成可重定位目标文件
  • 链接阶段
    进行符号解析和重定位,将可重定位目标文件链接成可执行目标文件

我们通过编译过程中的第一个环节:预处理之后,此时就把.h文件展开到.cpp文件当中,所以此时两个.cpp文件里面就分别包含了函数模板的声明(Test.cpp中包含了函数模板的声明和实现,main.cpp中包含了函数模板的声明和调用),这两个文件经过编译之后会生成汇编代码,经过汇编之后会生成符号表,但是在汇编这一步中,编译器会生成符号表,符号表里面存着各各函数的地址,在链接的时候由main函数中调用的Add函数去符号表里寻找地址,这时出错了! 符号表里面这时没有存Add的地址, 因为在编译过程中两个文件是独立的,正因为是独立的,在编译阶段模板才没能够进行实例化,因此符号表中并不会生成Add的地址,所以分离编译模板在链接时出错了,因为链接时会去调用Add,但是没有地址,链接出错。


解决方案:

1、显式实例化

//在声明的.cpp文件里面显式实例化模板
#include"Test.h"

template<class T>
T Add(const T& num1, const T& num2)
{
	cout << "T Add(const T& num1, const T& num2)" << endl;
	return num1 + num2;
}

//显式实例化模板
template 
int Add<int>(const int& num1, const int& num2);

缺陷:显式实例化只能实例化一种类型,当遇到操作数是其他类型,还需要进行其他类型的显式实例化

1、分离式编译

将声明和定义放在同一个.h文件里面,这样预处理的时候就可以让模板的声明、定义、调用出现在一个.cpp文件中,进而编译过程就可以实例化模板

你可能感兴趣的:(C++,c++)