# Template Resolver 那首先,就是更好的模板支持,这也是我最开始想要 clangd 支持的特性。具体来说目前在处理模板上有什么问题呢? 以代码补全为例,考虑如下的代码,`^`代表光标位置: ```cpp template void foo(std::vector vec) { vec.^ } ``` 在 C++ 中,如果一个类型依赖于模板参数,那么在模板实例化之前,我们并不能对它做出任何准确的假设。例如这里的`vector`即可能是主模板也可能是`vector`的偏特化,选哪一个呢?对于代码编译来说,准确性永远是最重要的,不能使用任何可能导致错误的结果。但是对于语言服务器来说,提供更多可能的结果往往比什么都不提供更好,我们可以假设用户在更多时候使用主模板而不是偏特化,从而基于主模板来提供代码补全的结果。目前 clangd 也确实是这么做的,在上述情况下它会根据`vector`的主模板为你提供代码补全。 再考虑一个更加复杂的例子: ```cpp template void foo(std::vector> vec2) { vec2[0].^ } ``` 从用户的角度来说,这里也应该提供补全,毕竟`vec2[0]`的类型不也是`vector`吗?和前面一个例子一样。但是 clangd 在这里却不会为你提供任何补全,问题出在哪里?根据 C++ 标准,`std::vector`的`operator[]`返回的类型是`std::vector::reference`,这其实是一个 [dependent name](https://en.cppreference.com/w/cpp/language/dependent_name),它的结果似乎相当直接,就是`T&`。但是 libstdc++ 中它的定义却嵌套了十几层模板,似乎是为了兼容旧标准?那为什么 clangd 不能处理这种情况呢? 1. 它基于主模板假设,不考虑偏特化可能会使查找无法进行下去 2. 它只进行名称查找而不进行模板实例化,就算找到了最后的结果,也没法把它和最初的模板参数映射起来 3. 不考虑默认模板参数,无法处理由默认模板参数导致的依赖名 尽管我们可以对标准库的类型开洞来提供相关的支持,但是我希望用户的代码能和标准库的代码有相同的地位,那么我们就需要一种通用的算法来处理依赖类型。为了解决这个问题,我编写了一个伪实例化器(pseudo instantiator)。它能在没有具体类型的前提下对依赖类型进行实例化,从而达到化简的目的。比如上面这个例子里面的`std::vector>::reference`就能被化简为`std::vector&`,进一步就能为用户提供代码补全选项。