[c++11新特性]12-类型萃取

时间:2022-12-07 20:00:16

​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::*)()>