为何代码在MSVC C++17编译通过,C++20下编译失败?
C++20下MSVC编译Json类报错的原因与修复方案
问题场景
这段简化的头文件式Json库代码,在MSVC的C17模式下编译正常,但切换到VS2022的C20模式时,Json::Get()中的return语句会触发编译错误。移除四个迭代器类中的objectIter_成员后,C++20模式下又能正常编译,但该成员是迭代器功能必需的,无法移除。
最小可复现示例(MCVE)
#include <map> #include <string> class Json; class Json_iterator; class Json_reverse_iterator; class Json_const_iterator; class Json_const_reverse_iterator; class Json_iterator { std::map<std::string, Json>::iterator objectIter_; }; class Json_reverse_iterator { std::map<std::string, Json>::reverse_iterator objectIter_; }; class Json_const_iterator { std::map<std::string, Json>::const_iterator objectIter_; }; class Json_const_reverse_iterator { std::map<std::string, Json>::const_reverse_iterator objectIter_; }; class Json final { public: Json &Get(std::string const &key) { auto const it = object_.find(key); return it->second; } private: std::map<std::string, Json> object_; }; int main() { Json json; Json child = json.Get("foo"); return 0; }
编译错误信息
error C2079: 'std::pair<const std::string,Json>::second' uses undefined class 'Json'
原因分析
问题根源在于C20标准对模板类型实例化的规则更严格,MSVC在C20模式下严格执行了这一标准:
- 当你在四个迭代器类中声明
std::map<std::string, Json>::iterator这类成员时,编译器需要确定该迭代器类型的大小、布局以及关联的类型信息。而std::map的迭代器指向的是std::pair<const std::string, Json>,这要求Json必须是完整类型(即已经完成类定义)。 - 但在原代码中,迭代器类的定义位于
Json类之前,此时Json仅为前向声明,属于不完整类型。C17模式下,编译器可能延迟了对迭代器类型的实例化,直到实际使用迭代器的代码出现(此时Json已经完整定义),因此没有报错;但C20模式下,编译器在处理迭代器类的成员声明时就需要Json是完整类型,从而触发了错误。
修复方案
方案1:调整类定义顺序
将Json类的完整定义移到四个迭代器类之前,这样当定义迭代器类时,Json已经是完整类型,std::map<std::string, Json>的迭代器类型就能正确实例化。修改后的代码如下:
#include <map> #include <string> class Json; class Json_iterator; class Json_reverse_iterator; class Json_const_iterator; class Json_const_reverse_iterator; class Json final { public: Json &Get(std::string const &key) { auto const it = object_.find(key); return it->second; } private: std::map<std::string, Json> object_; }; class Json_iterator { std::map<std::string, Json>::iterator objectIter_; }; class Json_reverse_iterator { std::map<std::string, Json>::reverse_iterator objectIter_; }; class Json_const_iterator { std::map<std::string, Json>::const_iterator objectIter_; }; class Json_const_reverse_iterator { std::map<std::string, Json>::const_reverse_iterator objectIter_; }; int main() { Json json; Json child = json.Get("foo"); return 0; }
方案2:将迭代器类改为Json的嵌套类
把四个迭代器类作为Json类的嵌套类,这样在定义嵌套类时,外层的Json已经是完整类型,满足std::map迭代器对类型完整性的要求。示例代码:
#include <map> #include <string> class Json final { public: class iterator; class reverse_iterator; class const_iterator; class const_reverse_iterator; Json &Get(std::string const &key) { auto const it = object_.find(key); return it->second; } private: std::map<std::string, Json> object_; public: class iterator { std::map<std::string, Json>::iterator objectIter_; }; class reverse_iterator { std::map<std::string, Json>::reverse_iterator objectIter_; }; class const_iterator { std::map<std::string, Json>::const_iterator objectIter_; }; class const_reverse_iterator { std::map<std::string, Json>::const_reverse_iterator objectIter_; }; }; int main() { Json json; Json child = json.Get("foo"); return 0; }
内容的提问来源于stack exchange,提问作者dgnuff
相关产品推荐
相关产品推荐

