(C++模板编程):std::void_t(上)

目录

std::void_t

源码分析和常规范例

判断类中是否存在某个类型别名

判断类中是否存在某个成员变量

判断类中是否存在某个成员函数

std::void_t

源码分析和常规范例

源码

// ALIAS TEMPLATE void_t
template <class... _Types>
using void_t = void;
  • 功能:能够检测到应用SFINAE(替换失败并不是一个错误)特性时出现的非法类型。换句话说,给进来的类型 _Types 必须是一个有效的类型,不能是一个非法类型

判断类中是否存在某个类型别名

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 
{
};
  • 调用
cout << HasTypeMem<NoInnerType>::value << endl;  //0
cout << HasTypeMem<HaveInnerType>::value << endl;  //1
  • 对于HasTypeMem<NoInnerType>::value 在面对特化版本时,由于没有成员类型别名type,T::type为非法类型,所以选择忽略特化版本,从而实例化泛化版本,泛化版本继承false_type,其父类有成员value,值为0;
  • 对于HasTypeMem<HaveInnerType>::value在面对特化版本时,有成员类型别名type,从而std::void_t<typename T::type>检测不是非法类型,其值为void,从而实例化特化版本,其父类有成员value,值为1;

上面类型成员固定了为type,下面通过宏进行一般化。

//带参数的宏定义,注意 反斜杠 表示下一行接着本行来,是本行的一部分
#define _HAS_TYPE_MEM_(parMTpNm) \
template <typename T,typename U = std::void_t<> > \
struct HTM_##parMTpNm : std::false_type {}; \
template <typename T> \
struct HTM_##parMTpNm<T, std::void_t<typename T::parMTpNm> > : std::true_type{};
  • 调用
_HAS_TYPE_MEM_(type);
_HAS_TYPE_MEM_(sizetype);

//_HAS_TYPE_MEM_(type);展开后
/*template <typename T, typename U = std::void_t<> > 
	struct HTM_type : std::false_type{}; 
template <typename T> 
struct HTM_type<T, std::void_t<typename T::type> > : std::true_type{};*/
//_HAS_TYPE_MEM_(sizetype);展开后
/*template <typename T, typename U = std::void_t<> >
struct HTM_sizetype : std::false_type {};
template <typename T>
struct HTM_sizetype<T, std::void_t<typename T::sizetype> > : std::true_type {};*/

cout <<  HTM_type<NoInnerType>::value << endl; 
cout << HTM_type<HaveInnerType>::value << endl;

cout << HTM_sizetype<NoInnerType>::value << endl;
cout << HTM_sizetype<HaveInnerType>::value << endl;

判断类中是否存在某个成员变量

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<decltype(T::m_i)> > : std::true_type 
{
};
  • 调用
cout << HasMember<NoInnerType>::value << endl;     // 1
cout << HasMember<HaveInnerType>::value << endl;   // 0
  • NoInnerType中有成员变量m_i。所以decltype(T::m_i)为int,是一个合法类型,因此实例化特化版本(实例化为:HasMember<HaveInnerType,void>)。
  • HaveInnerType没有成员变量m_i。所以decltype(T::m_i)是一个非法类型,从而std::void_t<decltype(T::m_i)>是一个非法类型,根据SFINAE特性,忽略特化版本,实例化特化版本。
  • 可通过宏使其一般化,可以判断是否存在其他的成员变量。

判断类中是否存在某个成员函数

struct NoInnerType
{
	int m_i;
};

struct HaveInnerType
{
	using type = int; //类型别名
	void myfunc() {}
};

//泛化版本
template <typename T, typename U = std::void_t<> >
struct HasMemFunc : std::false_type 
{
};
//特化版本
template <typename T>
struct HasMemFunc<T, std::void_t<decltype(std::declval<T>().myfunc())> > : std::true_type
{
};
  • 调用
cout << HasMemFunc<NoInnerType>::value << endl;
cout << HasMemFunc<HaveInnerType>::value << endl;

【注】泛化版本和特化版本到底编译器如何选择

  • 编译器通过一种复杂的排序规则来决定使用类模板的泛化版本还是特化版本。
  • 一般来说,void跟任何其他类型比,都是最后选择的那个。所以上面的泛化版本中默认参数为 U = std::void_t<> 等价于 U = void。
  • 实际情景下可以通过写测试代码进行判断。
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值