Files
clice/docs/zh/design/template-resolver.md

32 lines
2.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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>&`,进一步就能为用户提供代码补全选项。