gpt4 book ai didi

c++ - `void_t` 是如何工作的

转载 作者:IT老高 更新时间:2023-10-28 11:32:23 25 4
gpt4 key购买 nike

我在 Cppcon14 上观看了 Walter Brown 关于现代模板编程(Part IPart II)的演讲,他展示了他的 void_t。 SFINAE 技术。

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

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

以及以下用于检查名为 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
{ };

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

class A {
public:
int member;
};

class B {
};

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

1. has_member< A >

  • has_member< A , void_t< decltype( A::member ) > >
    • A::member存在
    • decltype( A::member )结构良好
    • void_t<>有效并计算为 void
  • has_member< A , void >因此它选择了专门的模板
  • has_member< T , void >并计算为 true_type

2. has_member< B >

  • has_member< B , void_t< decltype( B::member ) > >
    • B::member不存在
    • decltype( B::member )格式不正确并且静默失败 (sfinae)
    • has_member< B , expression-sfinae >所以这个模板被丢弃了
  • 编译器发现 has_member< B , class = void >以 void 作为默认参数
  • has_member< B >计算结果为 false_type

http://ideone.com/HCTlBb

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

最佳答案

1。小学类(class)模板

当你写 has_member<A>::value ,编译器查找名称 has_member并找到primary类模板,也就是这个声明:

template< class , class = void >
struct has_member;

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

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

2。专业类模板

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

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

编译器尝试匹配模板参数A, void使用部分特化中定义的模式:Tvoid_t<..>逐个。 首先,进行模板参数推导。上面的部分特化仍然是一个带有模板参数的模板,需要用参数“填充”。

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

在第二种模式中 void_t< decltype( T::member ) > ,模板参数T出现在不能从任何模板参数推导出的上下文中。

There are two reasons for this:

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

  • 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<>
struct has_member< A, void_t< decltype( A::member ) > > : true_type
{ };

类型void_t< decltype( A::member ) >现在可以评估了。它在替换后是良构的,因此不会发生替换失败。我们得到:

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

3。选择

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


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

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
{ };

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

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

但是我们的 has_member<A>::value 的模板参数列表现在是 <A, int> .参数与特化的参数不匹配,选择主模板作为后备模板。


(*) 恕我直言,该标准令人困惑地包括替换过程和在模板参数推导过程中显式指定模板参数的匹配。例如(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.

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

关于c++ - `void_t` 是如何工作的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27687389/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com