# Template Resolver

那首先，就是更好的模板支持，这也是我最开始想要 clangd 支持的特性。具体来说目前在处理模板上有什么问题呢？

以代码补全为例，考虑如下的代码，`^`代表光标位置：

```cpp
template <typename T>
void foo(std::vector<T> vec) {
    vec.^
}
```

在 C++ 中，如果一个类型依赖于模板参数，那么在模板实例化之前，我们并不能对它做出任何准确的假设。例如这里的`vector`即可能是主模板也可能是`vector<bool>`的偏特化，选哪一个呢？对于代码编译来说，准确性永远是最重要的，不能使用任何可能导致错误的结果。但是对于语言服务器来说，提供更多可能的结果往往比什么都不提供更好，我们可以假设用户在更多时候使用主模板而不是偏特化，从而基于主模板来提供代码补全的结果。目前 clangd 也确实是这么做的，在上述情况下它会根据`vector`的主模板为你提供代码补全。

再考虑一个更加复杂的例子：

```cpp
template <typename T>
void foo(std::vector<std::vector<T>> vec2) {
    vec2[0].^
}
```

从用户的角度来说，这里也应该提供补全，毕竟`vec2[0]`的类型不也是`vector<T>`吗？和前面一个例子一样。但是 clangd 在这里却不会为你提供任何补全，问题出在哪里？根据 C++ 标准，`std::vector<T>`的`operator[]`返回的类型是`std::vector<T>::reference`，这其实是一个 [dependent name](https://en.cppreference.com/w/cpp/language/dependent_name)，它的结果似乎相当直接，就是`T&`。但是 libstdc++ 中它的定义却嵌套了十几层模板，似乎是为了兼容旧标准？那为什么 clangd 不能处理这种情况呢？

1. 它基于主模板假设，不考虑偏特化可能会使查找无法进行下去
2. 它只进行名称查找而不进行模板实例化，就算找到了最后的结果，也没法把它和最初的模板参数映射起来
3. 不考虑默认模板参数，无法处理由默认模板参数导致的依赖名


尽管我们可以对标准库的类型开洞来提供相关的支持，但是我希望用户的代码能和标准库的代码有相同的地位，那么我们就需要一种通用的算法来处理依赖类型。为了解决这个问题，我编写了一个伪实例化器（pseudo instantiator）。它能在没有具体类型的前提下对依赖类型进行实例化，从而达到化简的目的。比如上面这个例子里面的`std::vector<std::vector<T>>::reference`就能被化简为`std::vector<T>&`，进一步就能为用户提供代码补全选项。
