zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C++模板不支持分离编译的问题

C++模板 支持 编译 分离 问题
2023-09-27 14:22:48 时间

前言

分离编译模式

一个项目如果有多个源文件.c组成,每个源文件单独编译,形成目标文件最后通过链接器将所有的目标文件链接起来,形成一个可执行的文件。

这种就是.h头文件和.c源文件配合使用的模式;

分离式编译的优势

如果工程巨大(比如linux内核源码) , 当你只修改了其中一个工程文件.c的时候,重新编译只需要编译改动过的文件就可以了,不需要将全部工程文件.cpp全部重新编译。

普通函数的分离编译(正常)

各文件代码如下:

//"fun.h"
int fun(int a);

//"fun.cpp"
#include"fun.h"
int fun(int  a)
{
    return a;
}


//main.cpp
#include<iostream>
#include"fun.h"

int main()
{
   	cout<<fun(1)<<endl;
    return 0;
}

结果:正常编译,正常运行;

模板函数的分离编译(出错)

各文件代码如下:

//"fun.h"
template<class T>
int fun(T a);

//"fun.cpp"
template<class T>
int fun(T a)
{
    return a;
}

//main.cpp
#include<iostream>
#include"fun.h"

int main()
{
   	cout<<fun(1)<<endl;
    return 0;
}

结果:编译出错

在这里插入图片描述

分析

实例化

实例化是指编译器使用函数(或者是类)模板为特定类型生成函数(类)定义。编译器不会为函数(或者类)模板生成定义,只有当我们为函数(或者类)模板指定了一个特定类型时,编译器才会生成。编译器为特定类型的函数(或者类)模板生成定义的行为被称为实例化。


普通函数在编译时就实例化生成fun.obj目标文件;

之后main.cpp编译的时候,看到了fun.h的头文件中fun函数的声明,没有看到定义,链接器,在链接时就看到了fun.obj目标文件中fun函数实例出来的对象,进行链接;


C++规定模板函数只有在使用时实例化:

fun.cpp定义了模版方法,但是没有使用,因此目标文件fun.obj中不会有实例化的模版方法。

main.cpp只能看到函数的声明,并且链接器链接时也找不到fun.obj中的fun函数对象, 因此报错;


解决方式

  1. 将模板的声明和定义放在一个.hpp文件里,或者像C语言一样,放在两个.h中 ,总之让编译器在一个源文件,比如main.c中能同时看到模板的定义和初始化就行;
  2. extern 控制实例化

拓展–extern关键字

extern"C"


extern “C”的作用就是告诉C++编译器,将指定的函数用C规则编译;

因为函数重载,函数名修饰规则不同的原因,如果C++中调用C文件,编译可能会出错;

extern "C"的主要功能是为了能够正确实现C++代码调用C语言代码


extern+变量

声明某个变量或者函数在外部文件,直接拿来用;


extern+模板–控制实例化

上面说的模板使用时实例化,那工程很大,每个文件都需要用这个模板。都需要实例化一份?额外开销会非常严重

这时候可以extern修饰他,意味着其他文件中的模板func也能在这里用,就是说公用一份,只需要在一个源文件中实例化这个模板即可,生成的obj其他文件能链接到!

提一嘴 新标准的显示实例化指定类型,也能解决上面模板多文件实例化太多造成额外开销的问题