m.s*_*.s. 3 c++ templates c++11
当阅读巴里的答案,以检查是否给定类型有一个内部模板重新绑定,心想:
为什么我们需要void_t呢?
为什么下面不工作?
#include <iostream>
template <typename X, typename Y = X>
struct has_rebind : std::false_type {};
template <typename X>
struct has_rebind<X, typename X::template rebind<int>> : std::true_type {};
struct A { };
struct B { template <typename > struct rebind { }; };
int main() {
    std::cout << has_rebind<A>::value << std::endl;
    std::cout << has_rebind<B>::value << std::endl;
}
产量
0
0
void_t 是一个黑客.
模板类专业化模式匹配的工作原理是主模板确定每个参数的种类(类型或值),默认值为:
template<class A, class B=A, class C=void>
struct whatever {};
要调用whatever<?...>,必须将有效参数与主要特化项匹配whatever<?...>.
在该测试通过之后,各种专业化模式匹配.所以假设你想要一个指针专门化.
template<class T>
struct whatever<T*, T*, void> {
template<?...>这里的参数列表不匹配:它只提供"自由变量"列表.模式匹配位于类名<?...>后面的模板参数列表中whatever.每个参数依次与发送的参数whatever匹配,并从该模式匹配"自由变量"(class T上图).
诀窍是你可以在这里放置不模式匹配的表达式,但依赖于其他模式匹配,然后生成一个新类型:
template<class T>
struct whatever<T*, typename std::add_const<T>::type*, void> {
第二个参数是(模板的add_const)依赖类型,因此不能进行模式匹配.通常,结果some_template<T>::type可以是图灵完全非内射计算:C++标准不需要反转,幸运的是编译器编写者.
编译器不会尝试 - 而是尝试T从其他模板参数中确定,然后替换T为该参数,并检查它是否与传递给的类型匹配whatever.
这里的技巧是替换失败(在直接上下文中)不会产生错误1.如果std::add_const<T>没有调用字段::type,而不是生成错误,它会改为说"好吧,这个模式不匹配".它会将这种专业化视为一组候选人中的一个可行的专业.
这个功能在C++的早期就被添加了,因为模板函数甚至可以贪婪地匹配基本类型,并且iterator::value_type如果你传递了intas ,尝试使用派生类型(例如)会失败iterator.使用此功能,int没有::value_type意味着"这不是某些模板函数的有效重载",而不是重载解析期间的语法错误.实际上,如果没有任何超载,模板函数几乎没用它,所以他们添加了SFINAE  - 替换失败不是错误.
一旦它出现,人们开始滥用它.  void_t试图利用它.
template<class T>
struct whatever<T*, T*, void_t<decltype(std::declval<T>().hello_world())>> {
void_t获取它的类型参数,并丢弃它们,并void 在依赖的上下文中生成(从而阻止表达式被用于推断T).它以首先计算类型参数2的方式这样做,这意味着如果生成的类型在直接上下文中导致失败,则会出现替换失败.
一点是,一旦你喂它,所产生的类型无关紧要void_t.t.hello_world()当我们定义主要特化时,我们甚至可能不知道应该是什么类型whatever<?...>.所以我们丢弃它并将其转化为void.为了匹配特化,类型必须匹配.因此我们将类型设置为void主要特化,并在特殊化中进行测试,然后将其传递void_t给扔掉类型结果.
我们也可以使用std::enable_if_t<?>一个编译时bool表达式,void当且仅当它bool为真时才产生类型(否则在直接上下文中失败).
从某种意义上说,void_t将有效类型表达式映射void到替换失败的无效类型表达式.  enable_if_t映射true到void,并false以取代的失败.在这两种情况下,您都需要void专门化中的类型来"使用"结果并正确匹配.
两者在SFINAE代码中都非常有用,这是一个强大的功能,可让您根据简单模式匹配之外的其他内容来决定使用哪种特化.
1最后我查了一下,这个要求没有明确在标准中!SFINAE规则适用于模板函数替换失败,并且每个人(包括编译器编写者和标准编写者)只是假设它应用于模板类替换失败.当然,标准很难读,我可能只是错过了它.
2在某一点上,各种编译器都不同意template<class...>using void_t=void;应该做的事情.如果传递的表达式是替换失败,则有些会失败:其他人会注意到表达式将被丢弃,并在检查替换失败之前将其丢弃.这在缺陷报告中得到澄清.
在这些方面:
template <typename X, typename Y = X>
struct has_rebind : std::false_type {};
template <typename X>
struct has_rebind<X, typename X::template rebind<int>> : std::true_type {};
默认类型(X)和您为specialization(typename X::template rebind<int>)提供的类型必须是相同的类型.由于void是一个非常好的"默认虚拟类型",它通常用作默认类型,我们void_t用来改变专业化中给出的任何内容
| 归档时间: | 
 | 
| 查看次数: | 415 次 | 
| 最近记录: |