`void_t` 是如何工作的

How does `void_t` work(`void_t` 是如何工作的)
本文介绍了`void_t` 是如何工作的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Cppcon14 上观看了 Walter Brown 关于现代模板编程的演讲(第一部分,第二部分),在那里他展示了他的 void_t SFINAE 技术.

I watched Walter Brown's talk at Cppcon14 about modern template programming (Part I, Part II) where he presented his void_t SFINAE technique.

示例:
给定一个简单的变量模板,如果所有模板参数都格式正确,则计算结果为 void:

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

以及以下特征,用于检查名为 member 的成员变量是否存在:

and the following trait that checks for the existence of a member variable called member:

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

// specialized as has_member< T , void > or discarded (sfinae)
template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : std::true_type
{ };

我试图了解这是为什么以及如何工作.因此,一个小例子:

I tried to understand why and how this works. Therefore a tiny example:

class A {
public:
    int member;
};

class B {
};

static_assert( has_member< A >::value , "A" );
static_assert( has_member< B >::value , "B" );

1. has_member<>

  • has_member
    • A::member 存在
    • decltype( A::member ) 格式良好
    • void_t<> 有效并计算为 void
    • has_member< A , void_t< decltype( A::member ) > >
      • A::member exists
      • decltype( A::member ) is well-formed
      • void_t<> is valid and evaluates to void

      2. has_member

      • has_member
        • B::member 不存在
        • decltype( B::member ) 格式错误并且无声地失败 (sfinae)
        • has_member 所以这个模板被丢弃了
        • has_member< B , void_t< decltype( B::member ) > >
          • B::member does not exist
          • decltype( B::member ) is ill-formed and fails silently (sfinae)
          • has_member< B , expression-sfinae > so this template is discarded

          http://ideone.com/HCTlBb

          问题:
          1. 我的理解正确吗?
          2. Walter Brown 指出默认参数必须与 void_t 中使用的类型完全相同才能工作.这是为什么?(我不明白为什么这些类型需要匹配,不是任何默认类型都可以吗?)

          Questions:
          1. Is my understanding of this correct?
          2. Walter Brown states that the default argument has to be the exact same type as the one used in void_t for it to work. Why is that? (I don't see why this types need to match, doesn't just any default type does the job?)

          推荐答案

          1.初级班模板

          当您编写 has_member::value 时,编译器查找名称 has_member 并找到 primary 类模板,即就是,这个声明:

          1. Primary Class Template

          When you write has_member<A>::value, the compiler looks up the name has_member and finds the primary class template, that is, this declaration:

          template< class , class = void >
          struct has_member;
          

          (在 OP 中,这是作为定义编写的.)

          (In the OP, that's written as a definition.)

          模板参数列表 与该主模板的模板参数列表进行比较.由于主模板有两个参数,但您只提供了一个,因此其余参数默认为默认模板参数:void.就好像你写了 has_member::value.

          The template argument list <A> is compared to the template parameter list of this primary template. Since the primary template has two parameters, but you only supplied one, the remaining parameter is defaulted to the default template argument: void. It's as if you had written has_member<A, void>::value.

          现在,将模板参数列表与模板has_member 的任何特化进行比较.仅当没有专业化匹配时,主模板的定义才用作后备.所以要考虑偏特化:

          Now, the template parameter list is compared against any specializations of the template has_member. Only if no specialization matches, the definition of the primary template is used as a fall-back. So the partial specialization is taken into account:

          template< class T >
          struct has_member< T , void_t< decltype( T::member ) > > : true_type
          { };
          

          编译器尝试将模板参数 A, void 与部分特化中定义的模式匹配:Tvoid_t<..>代码>一一.首先,进行模板参数推导.上面的部分特化仍然是一个模板,模板参数需要用参数填充".

          The compiler tries to match the template arguments A, void with the patterns defined in the partial specialization: T and void_t<..> one by one. First, template argument deduction is performed. The partial specialization above is still a template with template-parameters that need to be "filled" by arguments.

          第一个模式 T,允许编译器推导出模板参数T.这是一个微不足道的推论,但考虑像 T const& 这样的模式,我们仍然可以推导出 T.对于模式T和模板参数A,我们推导出TA.

          The first pattern T, allows the compiler to deduce the template-parameter T. This is a trivial deduction, but consider a pattern like T const&, where we could still deduce T. For the pattern T and the template argument A, we deduce T to be A.

          在第二种模式中 void_t,模板参数 T 出现在不能从任何模板参数推导出的上下文中.

          In the second pattern void_t< decltype( T::member ) >, the template-parameter T appears in a context where it cannot be deduced from any template argument.

          有两个原因:

          • decltype 中的表达式被明确排除在模板参数推导之外.我想这是因为它可以任意复杂.

          • The expression inside decltype is explicitly excluded from template argument deduction. I guess this is because it can be arbitrarily complex.

          即使我们使用了一个没有 decltype 的模式,比如 void_t<T >,则 T 的推导发生在解析的别名模板上.也就是说,我们解析别名模板,然后尝试从结果模式中推导出类型 T.然而,结果模式是 void,它不依赖于 T,因此不允许我们为 T 找到特定类型.这类似于试图对常数函数求反的数学问题(在这些术语的数学意义上).

          Even if we used a pattern without decltype like void_t< T >, then the deduction of T happens on the resolved alias template. That is, we resolve the alias template and later try to deduce the type T from the resulting pattern. The resulting pattern, however, is void, which is not dependent on T and therefore does not allow us to find a specific type for T. This is similar to the mathematical problem of trying to invert a constant function (in the mathematical sense of those terms).

          模板参数推导完成(*)现在替换推导的模板参数.这将创建一个如下所示的专业化:

          Template argument deduction is finished(*), now the deduced template arguments are substituted. This creates a specialization that looks like this:

          template<>
          struct has_member< A, void_t< decltype( A::member ) > > : true_type
          { };
          

          类型void_t<现在可以评估 decltype( A::member ) > .替换后格式良好,因此不会发生替换失败.我们得到:

          The type void_t< decltype( A::member ) > can now be evaluated. It is well-formed after substitution, hence, no Substitution Failure occurs. We get:

          template<>
          struct has_member<A, void> : true_type
          { };
          

          3.选择

          现在,我们可以将此特化的模板参数列表与提供给原始has_member<A>::value 的模板参数进行比较.两种类型完全匹配,因此选择此部分特化.

          3. Choice

          Now, we can compare the template parameter list of this specialization with the template arguments supplied to the original has_member<A>::value. Both types match exactly, so this partial specialization is chosen.

          另一方面,当我们将模板定义为:

          On the other hand, when we define the template as:

          template< class , class = int > // <-- int here instead of void
          struct has_member : false_type
          { };
          
          template< class T >
          struct has_member< T , void_t< decltype( T::member ) > > : true_type
          { };
          

          我们最终获得了相同的专业化:

          We end up with the same specialization:

          template<>
          struct has_member<A, void> : true_type
          { };
          

          但是我们has_member::value 的模板参数列表现在是.参数与专业化的参数不匹配,并且选择主模板作为后备.

          but our template argument list for has_member<A>::value now is <A, int>. The arguments do not match the parameters of the specialization, and the primary template is chosen as a fall-back.

          (*) 恕我直言,标准包括替换过程和在模板参数推导过程中明确指定的模板参数的匹配.例如(N4296 后)[temp.class.spec.match]/2:

          (*) The Standard, IMHO confusingly, includes the substitution process and the matching of explicitly specified template arguments in the template argument deduction process. For example (post-N4296) [temp.class.spec.match]/2:

          部分特化匹配给定的实际模板参数列表如果可以推导出偏特化的模板参数来自实际的模板参数列表.

          A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list.

          但这并不仅仅意味着必须推导出偏特化的所有模板参数;这也意味着替换必须成功,并且(看起来?)模板参数必须匹配部分特化的(替换)模板参数.请注意,我并不完全知道哪里标准指定了替换参数列表和提供的参数列表之间的比较.

          But this does not just mean that all template-parameters of the partial specialization have to be deduced; it also means that substitution must succeed and (as it seems?) the template arguments have to match the (substituted) template parameters of the partial specialization. Note that I'm not completely aware of where the Standard specifies the comparison between the substituted argument list and the supplied argument list.

          这篇关于`void_t` 是如何工作的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

Rising edge interrupt triggering multiple times on STM32 Nucleo(在STM32 Nucleo上多次触发上升沿中断)
How to use va_list correctly in a sequence of wrapper functions calls?(如何在一系列包装函数调用中正确使用 va_list?)
OpenGL Perspective Projection Clipping Polygon with Vertex Outside Frustum = Wrong texture mapping?(OpenGL透视投影裁剪多边形,顶点在视锥外=错误的纹理映射?)
How does one properly deserialize a byte array back into an object in C++?(如何正确地将字节数组反序列化回 C++ 中的对象?)
What free tiniest flash file system could you advice for embedded system?(您可以为嵌入式系统推荐什么免费的最小闪存文件系统?)
Volatile member variables vs. volatile object?(易失性成员变量与易失性对象?)