为何MSVC 19.13中/permissive-搭配/Zc:twoPhase-无法消除模板编译错误?
关于MSVC 19.13中/permissive-与/Zc:twoPhase-组合失效的编译问题
咱们先捋清楚你遇到的具体场景:用这段C++代码时,在MSVC 19.13里如果先加/permissive-编译选项,再搭配/Zc:twoPhase-,为啥没法像完全去掉/permissive-那样消除编译错误?
先贴出问题代码:
#include <stdio.h> template <unsigned int BYTES> class CBase { public: char Arr[BYTES]; int Fn1(void) { return Arr[1] ^ Arr[sizeof(Arr)-1]; } int Fn2(void) { return Arr[2] ^ Arr[sizeof(Arr)-2]; } }; template <unsigned int BYTES> class CDerived : public CBase<BYTES> { public: int FnSum(void) { return Fn1() + Fn2() + Arr[0]; // ERRORs: identifiers "Fn1" and "Fn2" and "Arr" are NOT found ! } }; int main(void) { CDerived<32> ddd; printf("%d\n", ddd.Fn1()); //No error here printf("%d\n", ddd.Fn2()); //No error here printf("%d\n", ddd.FnSum()); return (int)ddd.Arr[0]; //No error here }
先搞懂代码本身的问题根源
这段代码的核心矛盾在于依赖基类的名称查找规则:CDerived是模板类,它继承的CBase<BYTES>是依赖于模板参数BYTES的“依赖基类”。按照C++标准的两阶段名称查找规则:
- 第一阶段(模板定义时):编译器会查找不依赖模板参数的名称,而依赖基类里的成员(比如
Fn1、Fn2、Arr)属于依赖名称,这时候编译器不会去基类里找它们; - 第二阶段(模板实例化时):才会查找依赖名称,但前提是你得让编译器知道这些名称是依赖于模板参数的——要么用
this->前缀,要么显式用基类名限定(比如CBase<BYTES>::Fn1())。
而当你完全移除/permissive-时,MSVC会切换回旧的非标准查找规则:它会跳过第一阶段的严格检查,等到实例化时再去所有可见的作用域(包括依赖基类)里找名称,所以代码能正常编译。
为什么/permissive- + /Zc:twoPhase-组合没生效?
根据微软的官方说明,/permissive-会启用标准兼容模式,其中就默认开启了两阶段名称查找(相当于隐式设置了/Zc:twoPhase);而/Zc:twoPhase-的作用是关闭两阶段查找,理论上应该能覆盖/permissive-的隐式设置。但在MSVC 19.13里出现失效的情况,大概率是这个版本里的选项交互存在bug:
- 可能
/permissive-对两阶段查找的启用是强绑定的,后续的/Zc:twoPhase-无法完全覆盖它的规则; - 也可能是该版本中
/Zc:twoPhase-的实现不完善,没有彻底回退到旧的非标准查找逻辑。
解决办法
你有几个可选的方向:
- 修改代码符合标准:在CDerived的
FnSum里,把调用改成this->Fn1() + this->Fn2() + this->Arr[0],或者CBase<BYTES>::Fn1() + CBase<BYTES>::Fn2() + CBase<BYTES>::Arr[0]。这样不管是标准两阶段查找还是旧规则,都能正确找到基类成员; - 升级MSVC版本:后续的MSVC版本应该修复了这个选项交互的问题,升级后
/permissive- + /Zc:twoPhase-的组合应该能正常工作; - 放弃/permissive-选项:如果不需要标准兼容模式,直接移除
/permissive-,保持旧的编译规则即可。
内容的提问来源于stack exchange,提问作者George Robinson
相关产品推荐
相关产品推荐

