- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我在 Cppcon14 上观看了 Walter Brown 关于现代模板编程(Part I,Part 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
问题:
1.我的理解对吗?
2. Walter Brown 声明默认参数必须与 void_t
中使用的类型完全相同让它工作。这是为什么? (我不明白为什么这些类型需要匹配,不只是任何默认类型都可以工作吗?)
最佳答案
当你写 has_member<A>::value
,编译器查找名称 has_member
并找到primary类模板,也就是这个声明:
template< class , class = void >
struct has_member;
(在 OP 中,这是作为定义编写的。)
模板参数列表<A>
与此主模板的模板参数列表进行比较。由于主模板有两个参数,但您只提供了一个,其余参数默认为默认模板参数:void
.就好像你写了 has_member<A, void>::value
.
现在,将模板参数列表与模板的任何特化进行比较 has_member
.仅当没有特化匹配时,主模板的定义才用作后备。所以考虑了偏特化:
template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : true_type
{ };
编译器尝试匹配模板参数A, void
使用部分特化中定义的模式:T
和 void_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
likevoid_t< T >
, then the deduction ofT
happens on the resolved alias template. That is, we resolve the alias template and later try to deduce the typeT
from the resulting pattern. The resulting pattern, however, isvoid
, which is not dependent onT
and therefore does not allow us to find a specific type forT
. 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
{ };
现在,我们可以将这个特化的模板参数列表与提供给原始 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/
我正在尝试练习 void_t 的用法,但以下代码给出了编译错误。 is_fun 是 CompS 结构中的 typedef,所以我认为 Comp::is_fun 应该是有效的。 我在这里遗漏了什么吗?
我在 Cppcon14 上观看了 Walter Brown 关于现代模板编程(Part I,Part II)的演讲,他展示了他的 void_t。 SFINAE 技术。 示例: 给定一个计算结果为 vo
最近我尝试编写围绕 void_t 的包装器,如下所示: namespace detail { template struct applicable : std::false_type {}; tem
这个问题在这里已经有了答案: How does `void_t` work (3 个答案) 关闭 7 年前。 我有一个关于 void_t 元函数的问题,在这个视频中显示 https://www.yo
我尝试运行以下代码,它应该依赖于 void_t技巧,应该选择更专业的类模板(在本例中是第二个) #include #include template using void_t = void; t
我需要帮助的第一件事是解决下面的歧义。但是一旦歧义消失,我仍然需要知道是否有更简洁优雅的方式来实现 8 个特化。 #include template using void_t = void; te
考虑以下代码: template > struct is_invokable : std::false_type {}; template struct is_invokable>> : std::
cppreference 上有一个关于使用别名的例子。此示例失败,因为 int 没有成员 foo: template using void_t = void; template void_t f();
当我第一次学习如何检查类中的特定签名时,我被教导使用 std::void_t 并编写如下代码: template class HAS:public false_type{}; template cla
此刻,我是 using this method to check if a class has a method with a specific signature. 参加后Walter E. Bro
对于 C++14 中的成员检测,我使用了基于示例 here 的代码, 但它似乎不起作用。 一个完整的例子: #include template using void_t = void; templ
我想实现一个 has_no_duplicates评估为 std::true_type 的类型特征如果传递的可变类型列表没有重复类型。 static_assert(has_no_duplicates{}
在 C++17 中,我们有 std::void_t,这让 SFINAE 看起来更漂亮: template std::void_t foo() { /* stuff */ } 只有 T::prop 存
我在以下代码中得到了意外结果(第二个 static_assert 失败): #include template struct is_bananas : std::false_type {}; te
下面是试图在编译时检查默认构造函数是否存在的代码段。编译这个 clang version 11.0.0 Target: x86_64-apple-darwin19.6.0 Thread model:
我正在学习模板元编程,最近,我看到了关于 CPPConference 的演讲。关于 void_t。不久之后,我发现了detection idiom . 但是,我仍然很难理解其中任何一个(尤其是检测习惯
#include template struct IsIterator final : std::false_type {}; template struct IsIterator::iterato
前几天我遇到了这个。 #include #include using namespace std; template struct make_void { typedef void type; }
我如何实现 C++ detection idiom不使用 void_t?换句话说,我可以仅使用 C++03 功能实现 C++17 std::is_detected 等吗? UPD 根据定义,检测习语需
我在看Walter Brown's CppCon2014 talk on template metaprogramming的第二部分,期间他讨论了他的小说void_t<>的用途 build 。在他的演
我是一名优秀的程序员,十分优秀!