3

假设我有一个类/结构模板及其构造函数的显式推导指南。让这个类有两个模板参数,一个可以由推导指南推导,另一个不能。

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,即将扣除指南更改为

template <int Q, typename F>
Foo(F&& f) -> Foo<Q, typename alias2<F>::type>;

根据文档,现在这将引入一个非推导上下文(因为模板参数出现在范围运算符的左侧::),因此模板参数推导T=F应该失败(显然失败)。


问题1:如果这个理论是正确的,我能做些什么吗?假设我不想使用微不足道的身份别名,而是使用更复杂的类型转换,最终将typename transformation<Input>::result在演绎指南中具有 a 的形状。

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

template <typename T>
struct Foo {
    template <typename F>
    Foo(F&&) { }
};

template <typename T>
struct alias2 { using type = T; };

template <typename F>
Foo(F&& f) -> Foo<typename alias2<F>::type>;

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

template <typename T>
void some(typename alias2<T>::type) { }

int main() {
    Bar f{ 5 };
}

为什么即使这是非推导上下文,编译器也能够从 F 推导出 T?

4

1 回答 1

3

为了做你想做的事,C++ 必须能够反转一个图灵完备的子程序。

图灵完备程序不仅不可逆,而且无法确定给定的图灵完备程序是否可逆。您可以定义它们都是可逆的子语言,但这些子语言缺乏图灵完备的能力。

推导出Bar别名参数:

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

需要反转第二个模板参数alias<F>来查找F。Whenalias是一个普通的模板别名,这是 C++ 标准可能的、允许的和要求的。

alias2评估为 afoo<F>::type时,这样的构造能够进行图灵完备的计算。C++ 标准没有让编译器尝试反转这样的计算,而是统一说“不要尝试”。它通常使用“依赖类型”来阻止这种反转尝试。

在您的第二种情况下,BarFoo. Foo有扣分指南。该推导指南告诉您如何从FT,因此编译器不必反转任何潜在的图灵完备程序来确定T

C++ 语言有一堆措辞允许只是重命名参数等的模板别名就像它们是原始类型一样。本来连个玩具化名都能挡住一堆这样的推演;但这被发现是一个糟糕的计划。所以他们在标准中添加了文本来描述什么样的模板别名像这样“微不足道”,并修改了演绎规则的措辞,以便将它们视为透明的。


为了在类型推导期间反转任意图灵完备程序(实际上,几乎所有结构上非平凡的类型转换),您必须显式给出反转。

一旦你接受了这一点,它就变成了与语法的战斗,而不是概念上的战斗。

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

于 2020-07-15T13:23:52.227 回答