C++ - 模板分离编译

news/2025/2/22 10:55:45/

模板分离编译

 我们先来看一个问题,我们用 stack 容器的声明定义分离的例子来引出这个问题:
// stack.h

// stack.h
#pragma once
#include<deque>namespace My_stack
{template<class T, class Container = std::deque<T>>class stack{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_back ();}T top(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};
}

现有如上这个stack 类,我们把 push ()函数 和 pop()函数声明定义分离,如下所示:

 // stack.cpp

#include"stack.h"namespace My_stack
{template<class T, class Container>void stack<T, Container>::push(const T& x){_con.push_back(x);}template<class T, class Container>void stack<T, Container>::pop(){_con.pop_back();}
}

看似上述的分离是没有问题,但是,当我们编译的时候就报错了:

 上述报了一些 link 链接错误,这时候我们就很疑惑,反复查看声明和定义的链接关系,也看不出问题。

 我们调用 size ()这些没有进行声明定义分离的函数是没有 问题,问题就出在,我们什么定义分离的 push()和 pop()。

 我们在简单定义一个 A 类来对比:

// stack.hnamespace My_stack
{class A{public:void fun1();void fun2();};
}// stack.cppnamespace My_stack
{void func1()  // 定义了{}// 未定义// void func2()
}

在从源文件生成 可执行文件,有以下几步:
 

 上述的 push()函数 和 func2 ()函数都编译通过了,因为声明只是一个承诺,编译器在编译的时候,只会看函数有没有声明,如果这个函数有定义,那么就会去看这个函数的定义是否和声明一致,一致那么就编译通过了;没有定义有声明也是可以编译通过的。

但是在最后链接的时候:

  • fun1()函数有声明和定义,成功链接上了;
  • 而func2()有声明但是没有定义,所以没有连接上(这是正常的);
  • 但是此时的问题是 push()函数有声明和定义,但是却链接失败了。

 简单形容就是,我想买手机像func1()这个朋友借钱,它有声明,也就给我承诺了借我1000元,在最后买手机的时候,func1()也借给了我1000元。

而 func2()就是有声明,承诺给我500元,但是在买手机那一天,他却说它家里面有事,有需要用钱就没借我钱。

最后是 push()他也有声明,也承诺借钱给我,买手机那一天也确实向银行转了钱,但是我却没有收到。

最后push()的问题不是出在 我 和 push()身上,而是出在银行身上。

所以,现在你应该明白这个问题出来哪一个身上了,没错就是 编译器的问题。        

问题就出在编译的时候,因为地址是存在编译生成的 .s  文件当中,而在声明当中给的模版参数是 T,编译器在编译的时候不知道 这个 T 是什么类型,没错,就是出在了没有实例化上面。编译器都不知道实例化出的 T 的类型是什么,就无法生成这个函数的地址。

 func1()可以生成地址是因为 func1()不是模版函数,而push()是模版参数,只有实例化之后才能生成地址。

 解决方式

 第一种是显示实例化:

namespace My_stack
{template<class T, class Container>void stack<T, Container>::push(const T& x){_con.push_back(x);}template<class T, class Container>void stack<T, Container>::pop(){_con.pop_back();}templateclass stack<int>;
}

这里的 template 是语法规定,告诉编译器这里是 显示实例化。

 但是这个方式只能适用于单个类型,如果是其他类型的模版参数就不行了。

我们反观 top()这些函数,没有进行声明定义分离的函数,之所以能找到是因为这些函数的地址不需要再下面进行寻找,编译的时候就已经找到了地址(有定义就实例化,自然就找到了地址)。

而其他函数在后面需要找是因为,push()这些函数只有声明。

所以,如果我们想要进行声明定义分离的话,模版的分离不能分在两个问题,因为把声明和定义分离在同一个文件当中(如下代码所示):

#pragma once
#include<deque>namespace My_stack
{template<class T, class Container = std::deque<T>>class stack{public:void push(const T& x);void pop();//void push(const T& x)//{//	_con.push_back(x);//}//void pop()//{//	_con.pop_back ();//}T top(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};template<class T, class Container>void stack<T, Container>::push(const T& x){_con.push_back(x);}template<class T, class Container>void stack<T, Container>::pop(){_con.pop_back();}
}

 在库当中也是这样,把声明和定义,分离在同一个文件当中。

 当然,为了实现像之前一样的,用 .cpp 和 .h 两个文件实现的声明和定义分离的效果,我们可以将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。推荐使用这种
这里的 .hpp 表示的意思就是 .cpp 和 .h 的合体。当然不写成 .hpp 也是可以的。

 模版的优缺点

【优点】

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
  2. 增强了代码的灵活性

【缺陷】

  1.  模板会导致代码膨胀问题,也会导致编译时间变长
  2. 现模板编译错误时,错误信息非常凌乱,不易定位错误

 


http://www.ppmy.cn/news/1008370.html

相关文章

【2023】字节跳动 10 日心动计划——第六关

目录 1. 环形链表 II2. 有序数组中的单一元素 1. 环形链表 II &#x1f517; 原题链接&#xff1a;142. 环形链表 II 用哈希表判重即可。 class Solution { public:ListNode *detectCycle(ListNode *head) {unordered_set<ListNode*> st;while (head) {if (st.count(hea…

Linux命令200例:用Look一个进行文本搜索工具

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f3c6;本文已…

开发小程序的技术栈和注意事项和实习报告模板

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》采购供应链共享平台人员,财务规则对账人员,物流门禁计量系统对接人员,ERP事业部人员 公众号:山峯草堂,非技术多篇文章,专注于天道酬勤的 Java 开发问题、中国国学、传统文化和代码爱好者的程序…

kubernetes(一)

文章目录 1. k8s架构2. k8s集群搭建 1. k8s架构 2. k8s集群搭建

《Java面向对象程序设计》学习笔记——第 4 章 类与对象

​专栏&#xff1a;《Java面向对象程序设计》学习笔记 第四章 类与对象 4.1 编程语言的几个发展阶段 4.1.1 面向机器语言 机器语言、汇编语言为低级语言 4.1.2 面向过程语言 过程语言为高级语言 面向过程语言的核心是编写解决某个问题的代码块 4.1.3 面向对象语言 封装…

车载开发核心技术——SystemUI控制技术

SystemUI是指车载开发中的一个重要组件&#xff0c;它负责管理和控制车机的用户界面和交互功能。本文将详细介绍SystemUI的各项控制技术&#xff0c;包括音量控制、RingtonePlayer、电源管理、任务管理、通知栏和服务定制&#xff0c;并提供相关代码示例和解析。 一、音量控制…

【洋码头关键词获取商品列表技术解析】

洋码头关键词获取商品列表功能简介 洋码头的关键词获取商品列表功能是为了满足用户的个性化需求&#xff0c;提供更精准、更有针对性的商品推荐。用户可以输入关键词搜索&#xff0c;系统会根据关键词从海量的商品库中筛选出相关的商品列表&#xff0c;从而使用户更容易找到所需…

遮挡边界处的深度补全和双曲面外推

论文地址&#xff1a;Depth Completion with Twin Surface Extrapolation at Occlusion Boundaries 论文代码&#xff1a;https://github.com/imransai/TWISE 深度补全是从稀疏的已知深度值开始&#xff0c;为其余图像像素估计未知深度。 大多数方法将其建模为深度插值&#x…