如何在C ++ 20中为模板别名创建推论指南?

时间:2020-07-14 14:09:16

标签: c++ templates c++20 template-argument-deduction deduction-guide

假设我有一个类/结构模板及其构造函数的明确推导。让此类具有两个模板参数,其中一个可以通过推导指南推导,而另一个则不能。

<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">

<button class="w3-button w3-light-grey w3-large w3-right" onclick="w3_open() "><i class="fa fa-bars"></i></button>



<div class="w3-sidebar w3-bar-block w3-border-left top" style="display:block; width:10%; right:0;" id="mySidebar">
  <button onclick="w3_close()" class="w3-bar-item large">Close &times;</button>
  <a href="#" onclick="q1()" class="w3-bar-item w3-button " id="q1"> 1</a>
  <a href="#" onclick="q2()" class="w3-bar-item w3-button" id="q2"> 2</a>
  <a href="#" onclick="q3()" class="w3-bar-item w3-button" id="q3"> 3</a>
  <a href="#" onclick="q4()" class="w3-bar-item w3-button" id="q4"> 4</a>
</div>




<label class="xxlarge" id="question">
    
</label><br><br>


<input type="radio" name="choices" value="A" id="r1" class="radio"><label id="c1"></label><br><br>
<input type="radio" name="choices" value="B" id="r2" class="radio"><label id="c2"></label> <br><br>
<input type="radio" name="choices" value="C" id="r3" class="radio"><label id="c3"></label><br><br>
<input type="radio" name="choices" value="D" id="r4" class="radio"><label id="c4"></label><br><br>
<input type="radio" name="choices" value="E" id="r5" class="radio"><label id="c5"></label>

<br>
<br>
<br>

<button onclick="next()" class="btn green round">Save & Next</button>

如果我现在创建一个别名,该别名将显式指定以前不可推导的参数as far as I understand,则该别名的隐式生成推导指南应该能够完全推断出两个模板参数(根据标准模板参数推导的规则),即使在定义的类模板中未推导一种类型。

但是在我不使用template <int Q, typename T> struct Foo { template <typename F> Foo(F&&) { } }; template <typename T> using alias = T; template <typename T> struct alias2 { using type = T; }; template <int Q, typename F> Foo(F&& f) -> Foo<Q, alias<F>>; // deduction guide, but cannot deduce Q yet template <typename T> using Bar = Foo<1, T>; // create alias that fixes Q /* This should generate a deduction guide for Bar<T> by first "copying" Foo's deduction guide, deducing from Foo<Q, alias<F>> and Foo<1, T> that Q=1 and T=alias<F>=F, thus generating <template F> Bar(F&&) -> Bar<1, F>; if this was correct syntax. */ int main() { Bar f{ 5 }; } 而是使用alias的情况下该怎么办,即将推导指南更改为

alias2

根据documentation,现在会引入一个非推论上下文(因为模板参数在范围运算符template <int Q, typename F> Foo(F&& f) -> Foo<Q, typename alias2<F>::type>; left 中出现),因此template参数对::的推论应该失败(显然是does)。


问题1:如果这个理论是正确的,那么我能做些什么?假设我不想使用琐碎的身份别名,而是使用一个更复杂的类型转换,该类型转换最终将在推导指南中具有T=F的形状。

问题2:即使是现在,当我完全删除Q时,我的理论仍然失败,因为以下代码将被接受(GCC-10 / 11):

typename transformation<Input>::result

即使这是一个非推论上下文,为什么编译器还能从F推导出T?

1 个答案:

答案 0 :(得分:2)

要执行所需的操作,C ++必须能够反转图灵完整的子程序。

车削完成程序不仅是不可逆的,而且无法确定给定的图灵完成程序是否是可逆的。您可以在所有可逆的子语言中进行定义,但是这些子语言缺乏图灵完备的功能。

推导Bar别名参数:

template <typename T>
using Bar = Foo<1, T>;

需要反转第二个模板参数alias<F>才能找到F。如果alias是简单的模板别名,则C ++标准可能,允许和要求。

alias2的求和结果为foo<F>::type时,这样的构造就可以进行完整的计算。 C ++标准没有让编译器尝试来反转这种计算,而是统一说“不要尝试”。通常使用“依赖类型”来阻止这种反转尝试。

在第二种情况下,BarFoo的普通别名。 Foo有一个演绎指南。该推论行会告诉您如何从FT,因此编译器不必为了确定T而反转任何可能的图灵完整程序。

C ++语言有很多措辞,允许模板别名只是重命名参数或类似名称,就像它们在原始类型中一样。最初,甚至一个玩具别名也会阻止很多这种推论。但这被认为是一个坏计划。因此,他们在标准中添加了文字,以描述“琐碎”之类的模板别名,并修改了演绎规则的措词,以便将其视为透明。


要在类型推导过程中反转任意图灵完备的程序(实际上,几乎是任何结构上不重要的类型转换),必须显式进行反转。

一旦您接受了它,就变成了语法问题,而不是概念问题。

我不知道别名模板的用户定义模板推导指南的当前状态。上次我听说它不受支持,但最近没有检查。