看看这个
可以看看这个
《C++模板元编程技术与应用_荣耀(PPT).pdf》
编译成功:
template<bool _Test, class _Ty = void>
struct enable_if123;
template<class _Ty>
struct enable_if123<true, _Ty>
{ // type is _Ty for _Test
using type = _Ty;
};
int main()
{
if (1 == std::is_void<std::enable_if<true>::type>::value)
{
int i = 0;
i++;
}
enable_if123<true>::type t1;
}
最到位的理解
感觉这是我目前最到位的一个理解了:
给出另外一个版本,试图在偏特化的参数<>里切换成std::wstring,但编译出现了
错误,大意是推导不出来
关键是:都没有实例化的代码,光编译器前期的检查都通不过。
可以看出点端倪:偏特化的第二个参数,也要根据泛化版本的类型进行推导。
第二个参数,如果不给出具体类型,就是void,
你给出具体类型,比如std::is_floating_point<double>
注意,是先有类型,然后再推导。
怎么推导呢,比如用enable_if123<true,double>来实例化,那么第二个参数就是double
那么当编译器读到偏特化版本时,遇到
typename std::enable_if<std::is_floating_point<_Ty>::value, std::wstring>::type
它的内部还用到了using type = _Ty;
那么编译器就报错,推导不出来,为什么呢
虽然偏特化版本里的template<>列表里,有class _Ty,但相当于这个公式:
typename std::enable_if<std::is_floating_point<_Ty>::value, std::wstring>::type = double
可以这么认为:f(_Ty)=double,看如何推导出_Ty.
因为第二个参数,不管你怎么写,它的结果都应该是double
所以你再类里面用using type = _Ty,
就是用double来推导typename std::enable_if<std::is_floating_point<_Ty>::value, std::wstring>::type里面的_Ty。
别被template<class _Ty>给迷惑了,它不是double,而是通过double推导出来的
如果偏特化版本写成
template<class _Ty>
struct enable_if123<true, _Ty>
{ // type is _Ty for _Test
using type = _Ty;
};
那么就是要求解f(_Ty)=double,而f(_Ty)=_Ty,所以推导出来_Ty就是double
---------------------------------------------------
template<bool _Test, class _Ty = double>
struct enable_if123;
//{
//using type = _Ty;
//};
template<class _Ty>
struct enable_if123<true, typename std::enable_if<std::is_floating_point<_Ty>::value, std::wstring>::type>
{ // type is _Ty for _Test
using type = _Ty;
};
int main()
{
return 1;
}
error C2764: “_Ty”: 部分专用化“enable_if123<true,std::enable_if<std::is_floating_point<_Ty>::value,std::wstring>::type>”中未使用或不能推导出的模板参数
部分专用化中不使用模板参数。
这使得部分专用化不可用,因为无法推导模板参数。
------------------------------------------------------
看下面这个成功的例子:
实例化泛化版本的时候,第二个参数类型是std::is_floating_point<double>
那么找到偏特化的时候,f(_Ty)=std::is_floating_point<_Ty>
它的值就是具体类型:std::is_floating_point<double>
这个等式就是:
f(_Ty)=std::is_floating_point<_Ty>=std::is_floating_point<double>
所以,最后推导出的结果,_Ty就是double
最后由TypePrint<decltype(x)> b;打印出来的type类型,果然是double
template<typename T>
struct TypePrint;
template<bool _Test, typename _Ty = std::void_t<>>
struct enable_if123;
//{
// using type = _Ty;
//};
//偏特化的template列表里的参数,其实用不到?
template<typename _Ty>
struct enable_if123<true, std::is_floating_point<_Ty>>
{ // type is _Ty for _Test
using type = _Ty;
};
enable_if123<true, std::is_floating_point<double>>::type x;
TypePrint<decltype(x)> b;
std::ignore = b;
打印结果
error C2079: “b”使用未定义的 struct“TypePrint<double>”
说明偏特化匹配成功,并且x的类型是double
template<typename T>
struct my_is_pair : std::false_type
{
};
template<typename T, typename U>
struct my_is_pair<std::pair<T, U>> : std::true_type
{
};
template<typename T>
struct PrintTemplateType;
int main()
{
{
std::pair<int, double> pr;
std::enable_if<my_is_pair<std::pair<int, double>>::value, std::wstring>::type x;
PrintTemplateType<decltype(x)> b;
}
}
编译结果是
error C2079: “b”使用未定义的 struct“PrintTemplateType<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>”
说明x的类型就是std::wstring
那么过程就是很自然了:
编译器读到my_is_pair<std::pair<int, double>>,
先为泛化版本进行匹配,所以参数T就是std::pair<int, double>
那么来到偏特化版本my_is_pair<std::pair<T, U>> ,
把<>里面当作一个函数,f(T,U),它的推导结果就是std::pair<int, double>
也就是:f(T,U)=std::pair<T, U>=std::pair<int, double>
所以:
T: int
U: double
那么还有一个要注意的点:
template<typename T, typename U>
struct my_is_pair<std::pair<T, U>> : std::true_type
{
};
编译器对这种偏特化,丝毫不会去例会template后面尖括号里面的变量有几个,它最关心的是
my_is_pair后面尖括号里面变量的个数要和泛化版本里面template尖括号里参数的个数保持一致
感觉 啊,偏特化的第一个参数如果是给出的具体的类型时,后面的参数很难做稍微复杂的推导。第一个如果是普通
编译成功,匹配上的应该是泛化版本
第二种是偏特化,因为全特化的写法是template<>,
但这种情况下,第二种怎么才能匹配上呢
编译器做如何推导呢
template<bool _Test, typename _Ty = std::void_t<>>
struct enable_if123
{
using type = _Ty;
};
//偏特化的template列表里的参数,其实用不到?
template<typename _Ty>
struct enable_if123<true, std::is_floating_point<_Ty>>
{ // type is _Ty for _Test
using type = _Ty;
};
enable_if123<true>::type* x;
std::ignore = x;
-------------------------------------------------------
接上个例子,可以编译成功,并匹配std::is_floating_point
template<typename T>
struct TypePrint;
template<bool _Test, typename _Ty = std::void_t<>>
struct enable_if123;
//{
// using type = _Ty;
//};
//偏特化的template列表里的参数,其实用不到?
template<typename _Ty>
struct enable_if123<true, std::is_floating_point<_Ty>>
{ // type is _Ty for _Test
using type = _Ty;
};
enable_if123<true, std::is_floating_point<double>>::type x;
TypePrint<decltype(x)> b;
std::ignore = b;
打印结果
error C2079: “b”使用未定义的 struct“TypePrint<double>”
说明偏特化匹配成功,并且x的类型是double
-------------------------------------------------------
template<bool _Test, typename _Ty = std::void_t<>>
struct enable_if123;
//{
// using type = _Ty;
//};
//偏特化的template列表里的参数,其实用不到?
template<typename _Ty>
struct enable_if123<true, std::is_floating_point<_Ty>>
{ // type is _Ty for _Test
using type = _Ty;
};
enable_if123<true>::type* x;
std::ignore = x;
编译结果:
error C2027: 使用了未定义类型“enable_if123<true,void>”
note: 参见“enable_if123<true,void>”的声明
error C2065: “type”: 未声明的标识符
error C2065: “x”: 未声明的标识符
error C2065: “x”: 未声明的标识符
----------------------------------------------------------------
template<bool _Test, typename _Ty = std::void_t<>>
struct enable_if123;
//{
// using type = _Ty;
//};
//偏特化的template列表里的参数,其实用不到?
template<typename _Ty>
struct enable_if123<true, std::void_t<std::is_floating_point<_Ty>>>
{ // type is _Ty for _Test
using type = _Ty;
};
enable_if123<true>::type* x;
std::ignore = x;
结果。
偏特化版本里,第一个参数是true,第二个参数虽然有推导功能,但它实际上是void.
所以其实是个全特化版本。
error C3211: “enable_if123<true,void>”: 显式专用化正在使用部分专用化语法,请改用模板 <>
note: 参见“enable_if123<true,void>”的声明
error C2027: 使用了未定义类型“enable_if123<true,void>”
note: 参见“enable_if123<true,void>”的声明
error C2065: “type”: 未声明的标识符
error C2065: “x”: 未声明的标识符
error C2065: “x”: 未声明的标识符
看这个例子,好像和上面的enable_if又有点不同:
实例化的时候用了A<double,std::wstring>
所以偏特化的第二个参数,如果推导出来不是std::wstring,
这个偏特化版本是匹配不上的。
也就是说第二个参数如果没有推导,就像enable_if偏特化版本的第二个参数enable_if<true,_Ty>,
那么编译器就直接把泛型推导中的第二个参数赋给它。
如果有推导,那么推导结果必须和泛型的第二个参数一样。否则就匹配不上,或者报错。
template<class T, class Enabled = double>
class A;
template<class T>
class A<T, typename/*这个typename不可少*/ std::enable_if<std::is_floating_point<T>::value, std::wstring>::type> {
public:
A()
{
std::cout << "partial specialization\r\n";
}
}; // specialization for floating point types
int main123() {
A<double,std::wstring> a;
int m = std::is_floating_point<double>::value;
++m;
return 1;
}
运行结果:partial specialization
————————————————
根据上一个例子的解释,把偏特化的第二个参数的推导过程稍微改一下,把std::wstring改成T。
发现它匹配的是泛型版本。而泛化版本里没有定义value,所以编译出错。
因为什么呢,根据泛型版本推导出来T:double, Enabled:std::wstring
在用此组参数去检查偏特化版本的时候,发现第二个参数推导出来也是double
这样就成了T:double, Enabled:double
所以这个偏特化就匹配不上了,因为是以泛型的推导结果为主,所以这个偏特化就匹配不成功。
template<class T, class Enabled = double>
class A;
template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value, T>::type> {
public:
A()
{
std::cout << "partial specialization\r\n";
}
}; // specialization for floating point types
int main123() {
A<double, std::wstring > a;
int m = std::is_floating_point<double>::value;
++m;
return 1;
}
编译结果:
error C2079: “a”使用未定义的 class“A<double,std::wstring>”
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/tumu_C/article/details/137350079
struct NoInnerType
{
int m_i;
};
struct HaveInnerType
{
using type = int; //类型别名
void myfunc() {}
};
//泛化版本
template <typename T, typename U = std::void_t<> >
struct HasTypeMem : std::false_type //struct 默认是public ,class默认是private继承
{
};
//特化版本
template <typename T>
struct HasTypeMem<T, std::void_t<typename T::type> > : std::true_type
{
};
std::cout << HasTypeMem<NoInnerType>::value << std::endl; //0
std::cout << HasTypeMem<HaveInnerType>::value << std::endl; //1
按照上面的结论,
泛化版本对HasTypeMem<NoInnerType>的推导结果是HasTypeMem<NoInnerType,void>
以此为基础,去看偏特化版本:
偏特化版本的第一个参数是NoInnerType,第二个参数是void,没啥问题
但第二个void里面带有推导逻辑。由于NoInnerType里面没有type,所以推导不成功,最后采用的是泛化版本
看看这个例子,其实偏特化版本实例化之后的第二个参数,最后的结果也是void
刚好和泛型里面的对应上。只不过偏特化的第二个参数里面,带有推导的作用。
起到了一定的约束。
----------------------------------------------------------
要是不加约束呢,那么偏特化版本都能匹配成功。最后输出都是1
struct NoInnerType
{
int m_i;
};
struct HaveInnerType
{
using type = int; //类型别名
void myfunc() {}
};
//泛化版本
template <typename T, typename U = std::void_t<> >
struct HasTypeMem : std::false_type //struct 默认是public ,class默认是private继承
{
};
//特化版本
template <typename T>
struct HasTypeMem<T, std::void_t<> > : std::true_type
{
};
std::cout << HasTypeMem<NoInnerType>::value << std::endl; //1
std::cout << HasTypeMem<HaveInnerType>::value << std::endl; //1
将第一个参数设为需要推导的
再看一个例子,
template<typename _Test, typename _Ty = std::void_t<>>
struct enable_if123;
//{
//using type = _Ty;
//};
//偏特化的template列表里的参数,其实用不到?
template<typename _Ty>
struct enable_if123<typename _Is_floating_point<remove_cv_t<_Ty>>, _Ty >
{ // type is _Ty for _Test
using type = _Ty;
};
int main()
{
return 1;
}
编译通过
再看一个例子,
template<typename _Test, typename _Ty = std::void_t<>>
struct enable_if123;
//{
//using type = _Ty;
//};
//偏特化的template列表里的参数,其实用不到?
template<typename _Ty>
struct enable_if123<typename _Is_floating_point<remove_cv_t<_Ty>>, _Ty >
{ // type is _Ty for _Test
using type = _Ty;
};
int main()
{
enable_if123<true>::type x;
std::ignore = x;
return 1;
}
error C2974: 'enable_if123' : 模板 对于 '_Test'是无效参数,应为类型
note: 参见“enable_if123”的声明
error C2955: “enable_if123”: 使用 类 模板 需要 模板 参数列表
note: 参见“enable_if123”的声明
error C2039: “type”: 不是“enable_if123<_Test,_Ty>”的成员
error C2065: “type”: 未声明的标识符
error C2146: 语法错误: 缺少“;”(在标识符“x”的前面)
error C2065: “x”: 未声明的标识符
error C2065: “x”: 未声明的标识符
再看一个例子,
template<typename _Test, typename _Ty = std::void_t<>>
struct enable_if123;
//{
//using type = _Ty;
//};
//偏特化的template列表里的参数,其实用不到?
template<typename _Ty>
struct enable_if123<typename _Is_floating_point<remove_cv_t<_Ty>>, _Ty >
{ // type is _Ty for _Test
using type = _Ty;
};
int main()
{
enable_if123<bool>::type x;
std::ignore = x;
return 1;
}
error C2039: “type”: 不是“enable_if123<bool,void>”的成员
note: 参见“enable_if123<bool,void>”的声明
error C2065: “type”: 未声明的标识符
error C2146: 语法错误: 缺少“;”(在标识符“x”的前面)
error C2065: “x”: 未声明的标识符
error C2065: “x”: 未声明的标识符
编译通过,匹配上了泛化版本
template<typename _Test, typename _Ty = std::void_t<>>
struct enable_if123
{
using type = _Ty;
};
//偏特化的template列表里的参数,其实用不到?
template<typename _Ty>
struct enable_if123<typename _Is_floating_point<remove_cv_t<_Ty>>, _Ty >
{ // type is _Ty for _Test
using type = _Ty;
};
enable_if123<bool>::type* x;
std::ignore = x;