基于std::variant的递归变体结合std::map编译失败原因问询
GCC 7/8中基于std::variant的递归变体编译失败的根本原因
我们先看一下你提供的递归变体实现代码:
#include <map> #include <variant> struct recursive_tag; template <typename...> struct RecursiveVariant; template <> struct RecursiveVariant<int, std::map<int, recursive_tag>> : std::variant<int, std::map<int, RecursiveVariant<int, std::map<int, recursive_tag>>>> { using underlying = std::variant<int, std::map<int, RecursiveVariant<int, std::map<int, recursive_tag>>>>; using underlying::underlying; }; int main() { RecursiveVariant<int, std::map<int, recursive_tag>> rv; }
这段代码在GCC 7/8里编译失败,报错根源是编译器试图实例化std::pair<const int, recursive_tag>,但recursive_tag是不完全类型。而我们的预期是最终实例化std::map<int, RecursiveVariant<...>>对应的std::pair<const int, RecursiveVariant<...>>——这明明应该是完整类型,问题的核心原因如下:
- 编译器过早解析std::map的内部类型:在GCC 7/8的实现中,当你把
std::map<int, recursive_tag>作为RecursiveVariant的模板参数传入时,编译器会提前尝试解析这个std::map的关联类型(比如value_type,也就是std::pair<const int, recursive_tag>)。此时recursive_tag只是前向声明的不完全类型,而GCC 7/8的std::map实现还不支持以不完全类型作为value_type完成这类关联类型的解析,直接触发编译错误。 - 特化逻辑的执行顺序问题:你的代码意图是通过特化
RecursiveVariant,把模板参数里的recursive_tag替换成RecursiveVariant本身来实现递归变体。但编译器处理特化的模板参数列表(RecursiveVariant<int, std::map<int, recursive_tag>>)时,会先对std::map<int, recursive_tag>做初步的类型检查和关联类型实例化,这一步发生在你后续把recursive_tag替换成完整RecursiveVariant的逻辑之前。 - 补全recursive_tag的作用:当你给
recursive_tag补全定义(变成struct recursive_tag{};)后,它成为了完整类型,std::pair<const int, recursive_tag>可以被正常实例化,编译器对std::map<int, recursive_tag>的初步检查就能通过,后续特化中把recursive_tag替换成RecursiveVariant的过程也能顺利完成——此时RecursiveVariant的定义已经足够让std::map<int, RecursiveVariant<...>>的内部类型被正确解析。
补充说明:C++17标准其实放宽了对容器不完全类型的支持,但不同编译器的实现进度不同。GCC在9及以上版本中对std::map不完全类型的支持更完善,大概率不会出现这个问题。
内容的提问来源于stack exchange,提问作者Barry
相关产品推荐
相关产品推荐

