SFINAE: Understanding void_t and detect_if

SFINAE: Understanding void_t and detect_if

本文关键字:and detect if SFINAE Understanding void      更新时间:2023-10-16

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

但是,我仍然很难理解其中任何一个(尤其是检测习语,因为它基于 void_t(。我读了这篇博文,还有这篇 stackoverflow 文章,它对我有所帮助,但我仍然有一些问题。

如果我的理解是正确的,如果 void_t 中的表达式无效,它将使用以下表达式进行 SFINAE:

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_t?

好吧,我并不是说我自己完全理解了所有内容,但我会尽我所能回答:

因为 class 在这里是一个默认模板参数,可以表示独立于其类型的任意数量的参数?

近。此模板将使用一个或两个模板参数匹配任何实例化,因此所有表单都has_type_member<T>has_type_member<T, U>。这是由于

  • class匹配任何类型。但这并不特别(你也可以写class T你只是不需要这个名字,因为你没有在声明中引用它(每个模板一开始都匹配所有类型,只能通过参数的数量来区分。通常,我们要么通过一些SFINAE-magic(如enable_if(进行约束,要么稍后通过部分模板规范更好地拟合。
  • class = void匹配每种类型,如上所述,也根本没有类型,因为如果我们没有参数,void填充。

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

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

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

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

所以,无论类型和数量如何,...匹配所有内容,不是吗?实际上它只匹配一个有效的类型,如果不是,它将如何使用这个类型?(我知道它不使用类型,但它必须能够。并且它不能使用无效类型(。因此,它为我们提供了传递的模板参数是否有效的void。所以在我们的用例中:

如果T的成员类型T::type,则T::type是有效的类型,并且void_t<...>匹配它。所以我们在这一点上得到了void_t<T::type>,它计算为void,适合主要但更特别,所以我们接受它并得到一个true_type.

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

甚至有必要说类等于std::void_t<>吗?写还不够吗

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

如果没有,为什么?

是的,它会,它也在谈话中完成。void_t<>实际上是void.我认为void_t只是为了与第二个规范更一致。需要void_t,因为我们需要这个模板。