【问题标题】:SFINAE: Understanding void_t and detect_ifSFINAE:了解 void_t 和 detect_if
【发布时间】:2019-12-10 22:55:21
【问题描述】:

我正在学习模板元编程,最近,我在CPPConference 上看到一个关于 void_t 的演讲。不久之后,我发现了detection idiom

但是,我仍然很难理解其中任何一个(尤其是检测成语,因为它基于 void_t)。我阅读了this 的博文和this stackoverflow 的博文,对我有所帮助,但我仍有一些问题。

如果我的理解是正确的,如果 void_t 里面的一个表达式是无效的,就会用这个表达式 SFINAEd out:

template< class, class = std::void_t<> >
struct has_type_member : std::false_type { };

因为类在这里是一个默认模板参数,可以表示任意数量的参数,而与它们的类型无关?甚至有必要说 class 等于 std::void_t 吗?写不就够了吗

template< class, class = void >
struct has_type_member : std::false_type { };

如果不是,为什么?

但是,如果表达式有效,则该表达式将被称为评估为 void:

template< class T >
struct has_type_member<T, std::void_t<typename T::type>> : std::true_type { };

为什么一个有效的表达式会被评估为 void,这对我们有什么帮助?另外,为什么表达式需要有效才能匹配 void_t?

【问题讨论】:

    标签: c++ templates metaprogramming


    【解决方案1】:

    好吧,我自己并没有声称我已经完全理解了所有内容,但我会尽我所能回答:

    因为类在这里是一个默认模板参数,可以表示任意数量的参数,而与它们的类型无关?

    几乎。这个模板将匹配任何带有一个或两个模板参数的实例化,所以所有的形式都是has_type_member&lt;T&gt;has_type_member&lt;T, U&gt;。这是由于

    • class 匹配任何类型。但这并不特别(你也可以写 class T 你只是不需要名称,因为你没有在声明中引用它)每个模板首先匹配所有类型并且只能通过参数的数量来区分。通常,我们要么通过一些 SFINAE 魔术(如 enable_if)进行约束,要么稍后通过部分模板规范获得更好的拟合。
    • class = void 匹配所有类型,如上所述,也完全不匹配类型,因为如果我们没有参数,void 会填充。

    我们只会将此模板实例化为has_member_type&lt;T&gt;,因此这将始终是第一个匹配项,但可能不是最佳匹配项。但是作为第一个匹配,它告诉我们:第二个模板参数必须是void,因为所有进一步的匹配必须是部分规范。否则我们会变得模棱两可。想一想,如果表达式有效,第二个模板会给我们int,会发生什么。然后我们有两个匹配 has_type_member&lt;T, void&gt;has_type_member&lt;T, int&gt;,那么我们应该选择哪个?这就是为什么在成功的情况下,类型必须是void,然后也选择了这个重载,因为它更特别。

    为什么一个有效的表达式会被评估为 void,这对我们有什么帮助?另外,为什么表达式需要有效才能匹配 void_t?

    所以我已经回答了第一个问题的第二部分。关于第一个:想想void_t的定义:

    template<class...>
    using void_t = void;
    

    所以,... 匹配所有内容,无论类型和编号如何,不是吗?实际上它只匹配一个有效的类型,如果不匹配,它可能如何使用这种类型? (我知道它不使用该类型,但它必须能够使用。而且它不能使用无效类型)。因此,如果传递的模板参数有效,它会给我们void。所以在我们的用例中:

    如果T 有一个成员类型T::typeT::type 是一个有效类型并且void_t&lt;...&gt; 匹配它。所以此时我们得到void_t&lt;T::type&gt;,其计算结果为void,它适合主要但更特别,所以我们接受它并得到true_type

    如果我们没有类型成员怎么办?那么表达式T::type 无效,void_t&lt;...&gt; 无法匹配它,因此部分规范无效,所以我们不能选择它,但这没问题,因为替换失败不是错误,所以我们继续我们的已找到,主模板。

    甚至有必要说 class 等于 std::void_t 吗?写不就够了吗

    template< class, class = void >
    struct has_type_member : std::false_type { };
    

    如果不是,为什么?

    是的,它会,它也在谈话中完成。 void_t&lt;&gt; 字面意思是 void。我认为void_t 只是为了与第二个规范更加一致。这里需要void_t,因为我们需要这个模板。l

    【讨论】:

    • 好的,第一部分是有道理的。第一个参数是必需的,而第二个是可选的,它只是默认为 std::void_t。这意味着,默认情况下,这将匹配最多两个参数,然后,我们将检查是否有更适合我们的参数的选项。我写错了 void_t 的定义,所以我将其更改为:template&lt;class ...&gt; using void_t = void; 而且,据我所知,它匹配任何有效类型,而不是对象。您可以在对象上使用 decltype,以及使用 decltype 的类似技巧(如 std::decay 等)。
    • 我认为我看错了“如果类型有效则给我们无效”。在找到这个stackoverflow post 之后,我意识到我们不会将值转换为 void(这是不可能的),我们只会丢弃它,或者将它作废。因为强制转换为 void 会丢弃表达式。传递的类型仍然需要是实际类型,这是有道理的(尤其是当您查看解析器的工作方式时)。
    • 另外,我想,我对这部分不太清楚:Because class is here a default template parameter that can represent any number of parameters independent of their type? Is it even necessary to say that class equals to std::void_t&lt;&gt; ? Wouldn't it be enough to write...。我认为我可以使用这个问题后面的表达式来匹配任意数量的参数,因为默认的模板参数。现在回想起来,在这种情况下,这样的事情是不可能的,这是有道理的。
    • 我知道 void_t 只是 void 的别名,这更清楚地表明它应该与模板一起使用。现在,我知道 void_t 也可以匹配任意数量的参数,并且只是使它们无效。但是,我不明白为什么我们不直接使用 template&lt;class ...&gt; 之类的东西。
    • 如果你问,为什么我们不定义template&lt;class ...&gt; using void_t = void;,那么我的回答是:那是一样的。我只从谈话中复制了原始的...(我实际上不确定这是否只是一个速记、谈话中的错误或者可能是编译器扩展,查找它我发现没有关于是否允许template&lt;...&gt; 的来源) .标准的definestemplate&lt;class ...&gt;。我会在我的答案中编辑它。还有什么不清楚的吗? @Maki
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 1970-01-01
    • 2021-06-05
    • 1970-01-01
    相关资源
    最近更新 更多