c++11/14类型萃取
类型萃取
所谓类型萃取,就是依靠模板的方式,来判断一个类型是否拥有某些特性,比如A类型和B类型是否相同,C类型是否有某个成员变量,D类型是否有某个方法,或者根据不同的类型来执行不同的方法等。该功能主要是通过全特化来实现的。
全特化简介
所谓特化,就是模板类在使用具体的类型的时候,编译器根据模板类的具体类型生成实际代码的过程就叫做特化。而全特化,则是我们手动指定模板类型,并且重写该模板类的整个过程,此时编译器在遇到我们指定的这些类型时,就会使用我们自己编写的代码,而不是根据该类型自动生成该代码。如下:
template <typename T>
class Eat
{
public:
void eat()
{
std::cout << "eat food..." << std::endl;
}
};
template <>
class Eat<HotDog>
{
public:
void eat()
{
std::cout << "eat HotDog..." << std::endl;
}
};
class HotDog
{
};
class Rice
{
};
int main()
{
Eat<Rice> er; // 偏特化
Eat<HotDog> ehd; // 全特化
}
类型萃取实践
使用全特化实现POD类型判断
在c++11标准库中有一个std::is_pod
的模板类,用于判断一个类型是否是pod类型。使用方式如下:
#include <iostream>
#include <type_traits>
struct A
{
int m;
};
struct B
{
int m1;
private:
int m2;
};
struct C
{
virtual void foo();
};
int main()
{
std::cout << std::boolalpha;
std::cout << std::is_pod<A>::value << '\n';
std::cout << std::is_pod<B>::value << '\n';
std::cout << std::is_pod<C>::value << '\n';
}
这里我们借助全特化来实现一个自己的pod类。首先我们定义2个类——_FalseType
和_TrueType
,他们有一个相同的静态成员函数get
,只是简单的返回true
和false
。之后我们定义一个IsPODType
的模板类,并且让其在判断是否为pod类型的时候返回false
。之后,我们将我们认为的pod类型进行全特化,让其返回true。实现代码和测试代码如下:
#include <iostream>
#include <type_traits>
struct _FalseType
{
static constexpr bool get()
{
return false;
}
};
struct _TrueType
{
static constexpr bool get()
{
return true;
}
};
template <typename T>
struct IsPODType
{
using _pod_type = _FalseType;
static constexpr bool _value = _pod_type::get();
};
template <>
struct IsPODType<char>
{
using _pod_type = _TrueType;
static constexpr bool _value = _pod_type::get();
};
template <>
struct IsPODType<int>
{
using _pod_type = _TrueType;
static constexpr bool _value = _pod_type::get();
};
template <>
struct IsPODType<float>
{
using _pod_type = _TrueType;
static constexpr bool _value = _pod_type::get();
};
template <>
struct IsPODType<double>
{
using _pod_type = _TrueType;
static constexpr bool _value = _pod_type::get();
};
class Cup
{
};
int main(int argc,char **argv)
{
std::cout << "char is POD type:" << std::boolalpha << IsPODType<char>::_pod_type::get() << std::endl;
std::cout << "int is POD type:" << std::boolalpha << IsPODType<int>::_pod_type::get() << std::endl;
std::cout << "float is POD type:" << std::boolalpha << IsPODType<float>::_pod_type::get() << std::endl;
std::cout << "double is POD type:" << std::boolalpha << IsPODType<double>::_pod_type::get() << std::endl;
std::cout << "Cup is POD type:" << std::boolalpha << IsPODType<Cup>::_pod_type::get() << std::endl;
std::cout << "----------------------------------" << std::endl;
std::cout << "char is POD type:" << std::boolalpha << IsPODType<char>::_value << std::endl;
std::cout << "int is POD type:" << std::boolalpha << IsPODType<int>::_value << std::endl;
std::cout << "float is POD type:" << std::boolalpha << IsPODType<float>::_value << std::endl;
std::cout << "double is POD type:" << std::boolalpha << IsPODType<double>::_value << std::endl;
std::cout << "Cup is POD type:" << std::boolalpha << IsPODType<Cup>::_value << std::endl;
return 0;
}
// 输出
// char is POD type:true
// int is POD type:true
// float is POD type:true
// double is POD type:true
// Cup is POD type:false
// ----------------------------------
// char is POD type:true
// int is POD type:true
// float is POD type:true
// double is POD type:true
// Cup is POD type:false
判断2个对象类型是否相同
套路还是一样,我们先定义一个模板类is_same
,然后让它的_value
为false
,然后再使用全特化,将接收2个相同类型的模板的_value
设置为true
。代码如下:
template <typename T,typename U>
struct is_same
{
using type = _FalseType;
static constexpr bool _value = type::get();
};
template <typename T>
struct is_same<T,T>
{
using type = _TrueType;
static constexpr bool _value = type::get();
};
void test_same_type()
{
std::cout << "............test is same type............" << std::endl;
std::cout << "char,int8_t is same type:" << std::boolalpha << is_same<char,int8_t>::_value << std::endl;
std::cout << "char,int8_t is same type:" << std::boolalpha << std::is_same<char,int8_t>::value << std::endl;
std::cout << "char,char is same type:" << std::boolalpha << is_same<char,char>::_value << std::endl;
std::cout << "char,float is same type:" << std::boolalpha << is_same<char,float>::_value << std::endl;
std::cout << "float,_Float32 is same type:" << std::boolalpha << is_same<float,_Float32>::_value << std::endl;
std::cout << "Cup,int8_t is same type:" << std::boolalpha << is_same<Cup,int8_t>::_value << std::endl;
}
// ............test is same type............
char,int8_t is same type:false
// char,int8_t is same type:false
// char,char is same type:true
// char,float is same type:false
// float,_Float32 is same type:true
// Cup,int8_t is same type:false
从上面的输出可以看出,虽然char
和int8_t
的本质是一样的,但编译器还是认为他们不一样~~
删除类型的const、volatile属性
// 删除类型的volatile属性
template <typename T>
struct remove_volatile
{
using type = T;
};
template <typename T>
struct remove_volatile<volatile T>
{
using type = T;
};
template <typename T>
struct remove_const
{
using type = T;
};
template <typename T>
struct remove_const<T const>
{
using type = T;
};
template <typename T>
struct remove_cv
{
using type = typename remove_const<typename remove_volatile<T>::type>::type;
};
判断当前类型是否为整形
template <typename T>
struct is_interger_help
{
using type = _FalseType;
static constexpr bool _value = type::get();
};
template <>
struct is_interger_help<int>
{
using type = _TrueType;
static constexpr bool _value = type::get();
};
template <>
struct is_interger_help<char>
{
using type = _TrueType;
static constexpr bool _value = type::get();
};
template <>
struct is_interger_help<short>
{
using type = _TrueType;
static constexpr bool _value = type::get();
};
template <typename T>
struct is_interger:public is_interger_help<typename remove_cv<T>::type>
{
};
void test_is_interger()
{
std::cout << "............test is interger............" << std::endl;
std::cout << "int is interger:" << std::boolalpha << is_interger<int>::_value << std::endl;
std::cout << "char is interger:" << std::boolalpha << is_interger<char>::_value << std::endl;
std::cout << "short is interger:" << std::boolalpha << is_interger<short>::_value << std::endl;
std::cout << "float is interger:" << std::boolalpha << is_interger<float>::_value << std::endl; // 没有全特化float,因此输出false
std::cout << "Cup is interger:" << std::boolalpha << is_interger<Cup>::_value << std::endl;// 没有全特化float,因此输出false
}
// ............test is interger............
// int is interger:true
// char is interger:true
// short is interger:true
// float is interger:false //<-------- 没有全特化float,因此输出false
// Cup is interger:false
is_void
template <typename T>
struct is_void:is_same<typename remove_cv<T>::type,void>
{
};
void test_is_void()
{
std::cout << "............test is void............" << std::endl;
std::cout << "int is void:" << std::boolalpha << is_void<int>::_value << std::endl;
std::cout << "void is interger:" << std::boolalpha << is_void<void>::_value << std::endl;
}
// ............test is void............
// int is void:false
// void is interger:true
is_pointer
template <typename T>
struct is_pointer
{
using type = _FalseType;
static constexpr bool _value = type::get();
};
template <typename T>
struct is_pointer<T *>
{
using type = _TrueType;
static constexpr bool _value = type::get();
};
// 测试是否是指针
void test_is_pointer()
{
std::cout << "............test is pointer............" << std::endl;
std::cout << "int is void:" << std::boolalpha << is_pointer<int>::_value << std::endl;
std::cout << "int* is void:" << std::boolalpha << is_pointer<int*>::_value << std::endl;
std::cout << "void is interger:" << std::boolalpha << is_pointer<void>::_value << std::endl;
std::cout << "void* is interger:" << std::boolalpha << is_pointer<void*>::_value << std::endl;
std::cout << "Cup is void:" << std::boolalpha << is_pointer<Cup>::_value << std::endl;
std::cout << "Cup* is void:" << std::boolalpha << is_pointer<Cup*>::_value << std::endl;
}
// ............test is pointer............
// int is void:false
// int* is void:true
// void is interger:false
// void* is interger:true
// Cup is void:false
// Cup* is void:true
is_enum
目前为止,要判断是否为枚举类型,还需要依靠编译器来实现,在c++11标准库中借助了__is_enum
,实现如下:
template<typename _Tp>
struct is_enum
: public integral_constant<bool, __is_enum(_Tp)>
{ };
同样的,要实现is_union
、is_class
也需要借助于编译器,如下:
template<typename _Tp>
struct is_union
: public integral_constant<bool, __is_union(_Tp)>
{ };
/// is_class
template<typename _Tp>
struct is_class
: public integral_constant<bool, __is_class(_Tp)>
{ };
/// is_null_pointer (LWG 2247).
template<typename _Tp>
struct is_null_pointer
: public __is_null_pointer_helper<typename remove_cv<_Tp>::type>::type
{ };
标准库的类型萃取类型
- conditional<b, T, U> :条件运算,类似“?:”操作,根据 b 的真假决定返回 T 或 U
- common_type<T,...> :求多个类型的共通类型(类似于数字的最小公倍数)
- is_lvalue_reference :检查 T 是否是一个左值引用类型
- is_rvalue_reference :检查 T 是否是一个右值引用类型
- is_member_object_pointer :检查 T 是否是指向成员变量的指针
- is_member_function_pointer :检查 T 是否是一个成员函数指针
- is_reference :检查 T 是否是一个引用类型(左引用或右引用)
- is_arithmetic
- is_fundamental。
- is_compound
- is_member_pointer。
- is_scalar :检查 T 是否是标量类型,即算术类型、枚举、指针和成员指针
- is_object :检查 T 是否是实体对象类型,即引用、void 和函数之外的所有类型
- is_const :检查 T 是否被 const 修饰。
- is_volatile :检查 T 是否被 volatile 修饰。
- is_signed :检查 T 是否是有符号整数。
- is_unsigned :检查 T 是否是无符号整数。
- rank :如果 T 是数组,那么返回数组的维数,否则返回 0
- is_pod :检查 T 是否是一个 POD 类型①;
- is_empty :检查 T 是否是一个空类。
- is_abstract :检查 T 是否是一个抽象类(有纯虚函数)。
- is_polymorphic :检查 T 是否是一个多态类(有虚函数)。
- is_final : 检查 T 是否是一个 final 类(无法被继承)。
- has_nothrow_constructor:构造函数是否会抛出异常
- has_nothrow_copy:拷贝构造函数是否会抛出异常
- has_greater :检查 T 是否重载了 operator>
- has_less :检查 T 是否重载了 operator<
- has_equal_to :检查 T 是否重载了 operator==
- has_plus :检查 T 是否重载了 operator+
- has_minus :检查 T 是否重载了 operator−
- has_pre_increment :检查 T 是否重载了前置 operator++
- is_same<T, U> :检查 T 和 U 是否是相同的类型
- is_convertible<From, To> :检查 From 是否可隐式转型为 To 类型
- is_base_of<B, D> :检查 B 是否是 D 的基类,或两者相同
- is_virtual_base_of<B, D> :检查 B 是否是 D 的虚基类,不属于 C++11/14 标准
- add_const :返回 T const
- add_volatile :返回 T volatile
- add_cv :返回 T const volatile
- add_pointer :返回 T*
- add_lvalue_reference :对于对象或函数类型返回左值引用,通常是 T&,否则返回 T
- add_rvalue_reference :对于对象或函数类型返回右值引用,通常是 T&&,否则返回 T
- remove_const :移除 T 的顶层 const 修饰
- remove_volatile :移除 T 的顶层 volatile 修饰
- remove_cv :移除 T 的顶层 const 和 volatile 修饰
- remove_pointer :移除 T 的指针修饰(*)
- remove_reference :移除 T 的引用修饰(&或&&),注意不区分左引用或右引用
- make_signed :返回 T 相应的有符号整数类型,cv 修饰不变
- make_unsigned :返回 T 相应的无符号整数类型,cv 修饰不变
- remove_extent :移除数组的最顶层维度(降低一个维度
- remove_all_extents :移除数组的所有维度(变为 0 维的普通类型
struct dummy //一个简单的类
{
int x; //int 成员变量
double y; //double 成员变量
void func(){} //成员函数
};
is_member_object_pointer<int dummy::* >
is_member_object_pointer<double dummy::* >
is_member_function_pointer<void(dummy::*)()>