为什么模板不能分离编译?

答:如果模板的声明和定义不在同一文件内,那么编译在链接阶段的时候会因为找不到对应的函数地址而出错。
详解
下面的详解以模板函数为例,模板类同样适用。

①首先要知道编译的整个过程和每个过程做了什么事;
如果了解的可以跳过该步骤,如果不知道,可参看:
编译的整个过程:预编译、编译、汇编、链接
②了解编译的四个阶段以及每个阶段做的事情后,再来下面的具体例子:

//fun.h
#pragma once

#include<iostream>

using namespace std;

template <size_t N>
void fun();//模板函数

//fun.cpp
#include "test.h"

//模板函数
template <size_t N>
void fun()
{
    printf("模板函数:%d\n", N);
}
//test.cpp
#include "test.h"

int main()
{
    fun<10>();

    return 0;
}

这里写图片描述

上面的错误就是显示了链接错误;
如果模板函数的声明和定义不在同一个文件,即分离编译,则会出错。
预编译的时候,其中做的一件事就是“展开头文件”,针对上面的例子,下面的代码就是展开头文件之后的内容:

//fun.cpp
#include<iostream>
using namespace std;

template <size_t N>
void fun();//模板函数

//模板函数
template <size_t N>
void fun()
{
    printf("模板函数:%d\n", N);
}
//test.cpp
#include<iostream>
using namespace std;

template <size_t N>
void fun();//模板函数

int main()
{
    fun<10>();

    return 0;
}

预编译完成后生成fun.i和test.i文件
fun.i中有函数的声明和定义,但是没有函数的调用
test.i中有函数的声明和调用,但是没有函数的定义
④因此在编译阶段的时候,系统对fun.i和test.i文件分别做了这样的处理
fun.i:将fun.i中各函数的的地址保存在一个地址符表中,生成fun.s文件
test.i:在test.i文件中各个函数调用生成的call指令后面留一个位置,生成test.s文件
再经过汇编生成fun.o和test.o;
⑤再到最后一步链接的时候,是将多个.o文件链接生成一个可执行的.exe文件;
并且,在链接成一个文件之前,test.o文件会去fun.o文件中的地址符表里找对应的函数地址,填在call指令的后面
⑥至此,还需要补充的一点就是:如果模板没有被实例化,就不会生成代码
那么,原因就很明显了,因为声明和定义不在同一文件内,导致展开头文件后,fun.o里就只有模板的声明和定义,不存在实例化,所以fun.o文件的地址符表中就没有模板的地址,因此链接的时候,就会找不到地址而报错。

总结:真正原因就是上面的红字解释,如果将详解的内容看懂之后,再回过头看我在文章开头的“精简”的回答,应该就能看懂了。

如果对“预编译、编译、汇编、链接步骤为什么要分开进行?”感兴趣,请参见:

猜你喜欢

转载自blog.csdn.net/w_y_x_y/article/details/80299302