import{_ as i,c as a,o as e,ae as t}from"./chunks/framework.l7bN90nj.js";const c=JSON.parse('{"title":"Template Resolver","description":"","frontmatter":{},"headers":[],"relativePath":"zh/design/template-resolver.md","filePath":"zh/design/template-resolver.md"}'),l={name:"zh/design/template-resolver.md"};function p(n,s,h,k,d,r){return e(),a("div",null,[...s[0]||(s[0]=[t(`<h1 id="template-resolver" tabindex="-1">Template Resolver <a class="header-anchor" href="#template-resolver" aria-label="Permalink to &quot;Template Resolver&quot;">​</a></h1><p>那首先，就是更好的模板支持，这也是我最开始想要 clangd 支持的特性。具体来说目前在处理模板上有什么问题呢？</p><p>以代码补全为例，考虑如下的代码，<code>^</code>代表光标位置：</p><div class="language-cpp vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">cpp</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> &lt;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">typename</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&gt;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> foo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">vector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&gt; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">vec</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    vec.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">^</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><p>在 C++ 中，如果一个类型依赖于模板参数，那么在模板实例化之前，我们并不能对它做出任何准确的假设。例如这里的<code>vector</code>即可能是主模板也可能是<code>vector&lt;bool&gt;</code>的偏特化，选哪一个呢？对于代码编译来说，准确性永远是最重要的，不能使用任何可能导致错误的结果。但是对于语言服务器来说，提供更多可能的结果往往比什么都不提供更好，我们可以假设用户在更多时候使用主模板而不是偏特化，从而基于主模板来提供代码补全的结果。目前 clangd 也确实是这么做的，在上述情况下它会根据<code>vector</code>的主模板为你提供代码补全。</p><p>再考虑一个更加复杂的例子：</p><div class="language-cpp vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">cpp</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> &lt;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">typename</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&gt;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> foo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">vector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">std</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">vector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&gt;&gt; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">vec2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    vec2[</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">].</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">^</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><p>从用户的角度来说，这里也应该提供补全，毕竟<code>vec2[0]</code>的类型不也是<code>vector&lt;T&gt;</code>吗？和前面一个例子一样。但是 clangd 在这里却不会为你提供任何补全，问题出在哪里？根据 C++ 标准，<code>std::vector&lt;T&gt;</code>的<code>operator[]</code>返回的类型是<code>std::vector&lt;T&gt;::reference</code>，这其实是一个 <a href="https://en.cppreference.com/w/cpp/language/dependent_name" target="_blank" rel="noreferrer">dependent name</a>，它的结果似乎相当直接，就是<code>T&amp;</code>。但是 libstdc++ 中它的定义却嵌套了十几层模板，似乎是为了兼容旧标准？那为什么 clangd 不能处理这种情况呢？</p><ol><li>它基于主模板假设，不考虑偏特化可能会使查找无法进行下去</li><li>它只进行名称查找而不进行模板实例化，就算找到了最后的结果，也没法把它和最初的模板参数映射起来</li><li>不考虑默认模板参数，无法处理由默认模板参数导致的依赖名</li></ol><p>尽管我们可以对标准库的类型开洞来提供相关的支持，但是我希望用户的代码能和标准库的代码有相同的地位，那么我们就需要一种通用的算法来处理依赖类型。为了解决这个问题，我编写了一个伪实例化器（pseudo instantiator）。它能在没有具体类型的前提下对依赖类型进行实例化，从而达到化简的目的。比如上面这个例子里面的<code>std::vector&lt;std::vector&lt;T&gt;&gt;::reference</code>就能被化简为<code>std::vector&lt;T&gt;&amp;</code>，进一步就能为用户提供代码补全选项。</p>`,10)])])}const E=i(l,[["render",p]]);export{c as __pageData,E as default};
