当前位置: 首页 > news >正文

继承类模板:函数未在模板定义上下文中声明,只能通过实例化上下文中参数相关的查找找到

案发现场:

还是先来看代码,给出具体的问题场景。

	template<class T>class stack : public std::vector<T>{public:void push(const T& x){push_back(x);}//void pop()//{//	//	func();//}//const T& top()//{//	//}//bool empty()//{//	//}};

这是一段写在“继承”里的一段代码:我尝试定义一个类stack继承一个类模板——与平常继承一个具体的类不同。因为继承,派生类stack也是一个模板类;因为继承,派生类stack<T>继承了基类vector<T>的成员变量和成员函数,就包括了push_back(T)。于是,派生类stack<T>就可以顺理成章调用它继承来的push_back,这个push_back恰好就可以实现它需要的push功能。

于是,由于继承,由于功能的契合,一切都那么顺理成章。那么在我实例化出这个派生类后,是否能正常push呢?

注:为了达到效果,我先只留下push函数并进行测试。

测试代码如下:

	void test3(){stack<int> s;s.push(10);}

实例化了stack<int>类的对象s,接着我想入栈,也就是push一个10。

我们编译、运行一下:

图1 编译信息

一切源头都是 test.cpp(162)行:

图2 出错代码162

真相究竟是什么?

报错信息:“push_back”: 找不到标识符”。据我前文分析:

现在这段话就一个词不对——“顺理成章”。

何出此言?

 编译器是从上到下处理代码(预处理,编译,汇编,运行)。预处理:头文件展开/宏替换/条件编译/去掉注释;编译:语法分析、将代码转换成汇编代码。好,打住(详略得当(bushi)

看到这个处理顺序和方式,可以知道刚刚报错信息正是走到编译阶段语法分析失败的。


真相就是:编译器从头开始扫代码初步扫到模板定义阶段时,它有个规矩:凡是依赖模板参数的名字(dependent name),在模板定义阶段一律“延迟”到实例化时再查。而不依赖模板参数的T,编译器直接在当前可见域里找不到就报错。

如果这样写,就是“依赖模板参数的名字

	...ic:void push(const T& x){vector<T>::push_back(x);// 依赖模板参数T,加了作用域限定符}};

我们现在的版本,单看push_back(x)就不依赖模板参数T。编译器也不去猜你基类有没有这个函数了,直接认定这个函数(push_back)在当前类域里就有定义,就傻乎乎去找——没找到,自然就““push_back”: 找不到标识符”。

为什么不去猜?因为它懒。其实不是,假如此时它还不报错,姑且它押宝在实例化基类后再实例化派生类,派生类继承了成员函数。它再去基类找,如果还是找不到,浪费效率还找不到,它就是哑巴吃黄连——所以,它选择依赖T的成员或者函数,就直接当作本作用域函数了。写程序的我们就应该提前给它来一镇定剂,标注好作用域限定符——让它先稍安勿躁,不要着急去检查push_back。等实例化(编译器扫到stack<int> s)后,派生类对象自然而然就继承了函数,顺理成章顺着继承树(vector<T>::这条线)就找到了。

图3 成功编译
图4 成功运行

终究是错付了—按需实例化

如果按上面这种检查机制,那么我可以这样戏耍编译器——给一个本身不是基类的成员函数,也更没有在派生类里定义的函数,加上基类作用域限定符。这样编译器就不会在刚扫描这行代码报错。

但我们知道在实例化后编译器后面就会追查到这个函数并不存在。

这么说也就这么做:

template<class T>class stack : public std::vector<T>{public:void push(const T& x){vector<T>::push_back(x);}void pop(){// func()我胡乱取的,为了躲过编译器的编译,我加了vector<T>::,骗了它:等它实例化出stack<int> s就会找到func()vector<T>::func();}
};
	void test3(){stack<int> s;s.push(10);s.pop();// 调用一下,肯定要露馅。}
图5 编译报错

罢了罢了,意料之中。我们现在尝试,不调用pop()呢?

void test3(){stack<int> s;s.push(10);}
图6:不调用pop(),成功编译
图7 运行成功

 

编译骗过去了,运行成功了。—— 反推回去,只能说明:编译器并没有回去沿着vector<T>去检查func()是否如实存在。在stack<int> s;实例化了基类vector<T>,再实例化了派生类stack<T>;在s.push();实例化出了 stack<int>::push

  • 在该函数体里,遇到 vector<T>::push_back(x);
    → 此时第一次需要 vector<int>::push_back 的定义,于是编译器当场实例化 vector<int>::push_back。

而我们说的检查vector<T>::push_back是否存在也在这个实例化过程得以完成——拿到了函数的定义,实例化成功。而原本等待后期检查vector<T>::func()函数,编译器也不会返回检查了。——正是因为没有调用s.pop(),不会触发stack<int>::pop()实例化,接着func()的实例化。


编译器:你骗得我好苦,但你没调用,也无伤大雅——如果你一旦调用,我让你编不过去。

不开玩笑了,这就是模板“按需实例化”——只有被真正用到的模板成员(包括成员函数、成员类、基类子对象)才会被实例化;没被调用的成员,编译器连看都不看。

本章浓缩☕:依赖模板参数T成员/函数编译器的延后检查、按需实例化


文章转载自:

http://0I7GNxkr.njyxj.cn
http://KoM5fVIp.njyxj.cn
http://rwSG6dLR.njyxj.cn
http://BwCHU1Me.njyxj.cn
http://24I0jTEq.njyxj.cn
http://Gi1PxS00.njyxj.cn
http://ksxPkIQs.njyxj.cn
http://w7A10ZIj.njyxj.cn
http://CWo7pQI8.njyxj.cn
http://9h01CAFK.njyxj.cn
http://dPYNR03g.njyxj.cn
http://CnvpkAWB.njyxj.cn
http://oR7YBCVJ.njyxj.cn
http://RgPOszpd.njyxj.cn
http://4vClthn9.njyxj.cn
http://LeKjl3Ez.njyxj.cn
http://3EGpUrKt.njyxj.cn
http://wDm8R1WA.njyxj.cn
http://6xX3O5ie.njyxj.cn
http://972C6r6B.njyxj.cn
http://VNKBAlT0.njyxj.cn
http://MwPFG1wO.njyxj.cn
http://twBsy9MY.njyxj.cn
http://BO55qlxd.njyxj.cn
http://VQHY9V5M.njyxj.cn
http://gfHtf1sH.njyxj.cn
http://Iedw8brp.njyxj.cn
http://uaH3BKhe.njyxj.cn
http://166yfYvE.njyxj.cn
http://XyHYaGc8.njyxj.cn
http://www.dtcms.com/a/383071.html

相关文章:

  • 07-Redis 基础操作全攻略:从键管理到数据类型判断
  • 【linux】特殊权限
  • [数据结构] 排序
  • Python网络与多任务编程:TCP/UDP实战指南
  • Elasticsearch面试精讲 Day 17:查询性能调优实践
  • Go-zero 构建 RPC 与 API 服务全流程
  • CRI容器运行时接口
  • 《Python 自动化表单填写全攻略:从基础操作到实战案例》
  • 黑马程序员JVM基础学习笔记
  • 驰骋低代码BPM开发平台的组成部分
  • ubuntu22.04源码安装ffmpeg-4.4
  • 黑马Java进阶教程,全面剖析Java多线程编程,并发和并行,笔记02
  • 大数据毕业设计选题推荐-基于大数据的教育与职业成功关系可视化分析系统-Spark-Hadoop-Bigdata
  • Ubuntu Server 安装图形界面和通过Window远程桌面连接服务器(Xrdp)
  • 贪心算法在云计算虚拟机部署问题中的应用
  • macOS中找不到钥匙串访问
  • 基于FPGA实现LeNet-5(经典CNN识别手写数字)推理
  • 算法-双指针5.6
  • Eino Indexer 组件完全指南
  • 算法-双指针3.4
  • 【开题答辩全过程】以 “旧书驿站”微信小程序的设计与开发为例,包含答辩的问题和答案
  • Altium Designer使用精通教程 第七章(PCB输出)
  • 【秋招笔试】2025.09.13美团秋招算法岗真题\
  • LeetCode 2367.等差三元组的数目
  • 第16课:多模态Agent协作
  • 《网络攻防技术》第一章: 网络攻防概述
  • 消息语义一致性:Exactly-Once 之外的“效果等价”设计
  • SPI NOR Flash 的命令码详解
  • kafka--基础知识点--5.2--最多一次、至少一次、精确一次
  • Spark(1):不依赖Hadoop搭建Spark环境