ISO C++标准中检查依赖基类的规则为何制定?解析VC++编译器问题
为什么ISO C++标准要制定依赖基类的名字查找规则?
这个问题其实戳中了C++模板系统里一个很关键的设计点——咱们先从「依赖基类」的定义说起:当你写一个模板类D<T>继承自B<T>(这里B是依赖于模板参数T的基类),B<T>就是依赖基类,它的具体成员要等到模板被实例化时才会确定。
标准禁止在模板定义阶段的非限定名字查找中检查依赖基类,主要有两个核心原因:
- 避免编译阶段的不确定性与效率损耗:如果允许编译器在模板还没实例化时就去查依赖基类的成员,那麻烦就大了——不同的模板参数
T可能会让B<T>有完全不同的成员,编译器根本没法在定义阶段确定哪个f是你要调用的。这不仅会让编译逻辑变得极度复杂,拖慢编译速度,还很容易引入难以排查的二义性问题。 - 保证模板行为的可预测性:C++标准希望模板的名字绑定是「早绑定」的——在模板定义阶段就确定非限定名字的来源,而不是等到实例化时才临时查找。这样开发者在写模板的时候,就能明确知道自己调用的
f到底指向哪个函数,不会因为换了个实例化参数,突然出现名字绑定变化的情况,大幅降低调试难度。
再说说你遇到的VS2017的情况:那个「一致性模式开关」,其实就是VC++开始严格对齐标准规则了。比如你提到的D::g里调用f,而f来自依赖基类B——按照标准,这种非限定的f调用,编译器在模板定义阶段不会去依赖基类里找,所以这确实是源代码错误。
至于VC之前没诊断这个问题,是因为早期微软的模板实现比较宽松,为了兼容大量旧代码,也为了简化编译器实现,没有严格执行标准的名字查找规则。但随着C11、C++17等标准的推进,跨编译器移植的需求越来越高,微软才加入了这个一致性开关,让用户可以选择写出符合标准的代码,确保在不同编译器下行为一致。
顺便提一句,遇到这种情况的正确写法,要么用this->f()(通过this触发依赖查找,告诉编译器这个名字要等到实例化时去基类里找),要么显式指定B<T>::f()(不过这种写法要注意成员隐藏的问题)。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

