md,感觉enable_if的推导过程又迷糊了。这次好像又清晰了一些

看看这个

类模板相关知识-CSDN博客

可以看看这个

CSDN

 《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;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值