1.type_traits-类型萃取
(1)type_traits可以在一定程度上消除 switch-case 或者 if-else语句,降低程序的复杂度
(2)可以在编译期就检查出是否是正确类型
1.1基本的type_traits
定义编译期常量
struct GetLeftSize
{
static const int value =1;
}
或
struct GetLeftSize
{
enum { value = 1};
};
在c++11中定义编译期常量,无须自己定义static const int 或 enum类型,只需要从std::integral_constant派生:
template
struct GetLeftSize : std::integral_constant
{
};
可以通过 GetLeftSize::value来获取常量1,看下integral_constant的包装:
template
struct integral_constant
{
static const T value = v;
typedef T value_type;
typedef integral_constant
operator vaue_type() {return value;}
};
而true_type和false_type是integtal_constant的一个实例:
typedef integral_constant
typedef integral_constant
std::true_type和std::false_type分别定义了编译期的true和false类型
1.2类型判断的type_traits
template
struct is_integral;
检查T类型: bool char char16_t char32_t wchar_t short int long longlong,其中一种返回true。std::is_integral::value为true
is_void
is_floating_point 浮点类型(非指针)
is_array
is_pointer 指针类型(包括函数指针,不包括成员函数指针)
is_enum
is_union 是否为非union的class/struct
is_class
is_function 是否为函数类型
is_reference 是否为引用类型(左右引用都行)
is_arithmetic是否为整型和浮点型
is_fundamental 是否为整型、浮点、void、nullptr_t
is_object 是否为一个对象类型(不是函数 不是引用 不是void)
is_member_pointer 是否为成员函数指针类型
is_polymorphic 虚函数
is_abstract 抽象类
is_signed 有符号类型
is_unsigned 无符号类型
is_const 为const修饰的类型
通过 std::is_xxx::value来判断是否满足条件
std::is_const
std::is_const
1.3 traits之判断两个类型的关系
template
struct is_same; 判断两个类型是否相同
template
struct is_base_of; //判断Base是否为Derived的基类
template
struct is_convertible; 判断前面类型是否可以转换为后面类型
#include
#include
class A {};
class B : public A{};
class C{};
int mian()
{
bool b2a = std::is_convertible::value; // true
bool a2b = std::is_convertible::value; // false 不能向下转换
........
}
3.type_traits之 类型的转换
template
struct remove_const; 移除const
以下写法省略template
add_const
remove_reference
add_lvalue_reference
add_rvalue_reference
remove_extent // 移除数组顶层的维度
remove_all_extent //移除数组的所有维度
remove_pointer 移除指针
add_pointer 添加指针
decay 移除cv或添加指针
common_type 获取公共类型
获取转换后的类型是通过:
std::xx<>::type来获取类型的,非::value
根据模板参数类创建对象时,要注意移除引用:
template
typename std::remove_reference
{
typedef typename std::remove_reference
return new U();
}
在上述例子中,返回值和函数中typename是因为模板的嵌套从属类型,好好看模板基础吧!哈哈哈~~~~
模板参数类型可能是引用类型,而创建对象时,需要原始的类型,不能用引用类型,所以需要将可能的引用移除
移除cv引用类型(什么鬼东西,从来没有见到过,为了满足下荣誉感,还加上吧)
template
T* Create()
{
return new T();
}
int* p = Create
编译失败;需移除引用和cv符才能获取原始的类型int
template
typename std::remove_cv
{
typedef typename std::remove_cv
return new U();
}
代码较长,用decay来替代去除引用和cv(麻蛋,什么鬼,前面不是只说decay能去处cv,怎么又多出个引用啊,~~~):
template
typename std::decay
{
typedef typename std::decay
return new U;
}
cv : const volatile
decay的功能还不止为此,还可以用于数组和函数:
先移除T类型的引用,得到类型U,U定义为remove_reference
如果is_array::value为true,修改类型type为remove_extent::type*;
否则如果is_function::value为true,修改类型为add_pointer::type
否则修改类型为remove_cv::type,去处const 或 volatile
(上面的法则真是让人想吐,那个SB定义的!!)
int&& ---int
const int& -- int
int[2] -- int*
int(int) -- int(*)(int)
函数变成函数指针可以保存下来(但是函数变成函数指针到底指的是什么鬼?就是类似int(int)变成int(*)(int)吗?)
using FnType = typename std::decay
1.2 traits -- 根据条件选择
std::conditional 在编译期根据一个判断式选择两个类型中的一个:
template
struct conditional;
如果B为true,则conditional::type为T,否则为F
比较两个类型输出较大的那个:
typedef std::conditional<(sizeof(long long) > sizeof(long double)), long long, long double>::type max_size_t;
count< 1.3 traits--获取可调用对象返回类型 template ?? Func(F f, Arg arg) { return f(r); } 不能直接确定返回的类型;可以通过decltype来推断: template decltype((*(F*)0)((*(Arg*)0))) Func(F f, Arg arg) { } 前置的那么难理解啊?算了,还是看后置的吧 template auto Func(F f, Arg arg)->decltype(f(arg)) { return f(arg); } 上述代码在没有参数的情况下,是不能通过decltype来推导类型的 #include class A { A() = delete; public: int operator()(int i) { return i; } }; int main () { decltype(A()(0)) i =4; cout<
return 0; } 上述的代码会报错,因为A没有默认构造函数;对于没有默认构造函数的类型,如果希望推导其成员函数的返回类型,需要借助std::declval decltype(std::declval()(std::declval std::declval能获取任何类型的临时值,不管它是否有默认构造函数,因此,可以通过declval()来获取A的临时值; 注:declval获取的临时值引用不能用于求值,因此,需要用decltype来推断出最终的返回类型 c++11提供了另外一个trait函数,用来获取一个可调用对象,std::result_of,这个真的很吊的!!!!!!!!!!!!! std::result_of::type i = 4; 其实,std::result_of::type 实际上等价于 decltype(std::declval()(std::declval std::result_of的原型如下: tempalte class result_of std::result_of typedef std::result_of 这里说下函数对象的定义: 1.函数指针 2.具有operator()成员函数的类对象(仿函数) 3.可被转换为函数指针的类对象 4.类成员(函数)指针 例子就不举了,反正你也看不懂,哈哈哈~~~~~~~ result_of例子走起,真尼玛牛逼,,,,,但是一般类没有构造函数的情况比较少,那么是不是用decltype比较好些,不需要转化为可调用对象 example1: int fn(int) { return int(); } 函数 如果要对某个函数使用std::result_of,首先将函数转化为可调用对象 typedef std::result_of typedef std::result_of typedef std::result_of static_assert(std::is_same::value, "not equal"); true static_assert(std::is_same::value, "not equal"); true static_assert(std::is_same::value, "not equal"); true example2: template auto GroupBy(const vector { typedef decltype(keySelector(*(Person*) nullptr)) key_type; multimap std::for_each(vt.begin(), vt.end(), [&](const Person& person) { map.insert(make_pair(keySelector(person), person)); }); return map; } 可以看出键值是以Fn作为函数,Person对象作为输入参数来获取类型的。 但是decltype(keySelector(*(Person*) nullptr)) key_type 比较难懂啊??????ri 用result_of来替换 template multimap GroupBy(const vector { typedef std::result_of multimap std::for_each(vt.begin(), vt.end(), [&] (const Person& person) { map.insert(make_pair(keySelector(person), person)); }); return map; } 1.4 traits--根据条件禁用或启用 匹配重载函数: template void Fuc(T*){ } template void Fun(T){} int main() { Func(1); return 0; } 将会匹配第二个重载函数 template struct enable_if; 在判断条件B为true时,enable_if才有效,否则的话编译失败 template typename std::enable_if { return t; } auto r = foo(1); auto r1 = foo(1.2); auto r2 = foo("tess"); // 编译错误 std::enable_if 不仅可以作用于返回值,还可以作用于模板定义、类模板特化、入参类型的限定 // 判断第二个入参类型是否为integral类型(我感觉这句话有问题,因为判断T是否为整型,T也是第一个参数类型啊?你说是不是?) template T foo2(T t, typename std::enable_if { return t; } foo2(1,2); 可以 foo2(1," ");第二个参数错误 // 对模板参数T做了限定,T只能为integral类型 template T foo3(T t) { return t; } template class A; // 模板特化,对模板参数做了限定,模板参数只能为浮点型 template class A A A 要求:对于入参为arithmetic类型的返回 0 ,非arithmetic类型的返回1; template typename enable_if { cout< return 0; } template typename enable_if::value, int>::type foo(T& t) { cout< return 1; } std::enable_if的第二个参数是默认模板参数void类型,因此在函数没有返回值时,后面的模板参数可以省略 typename std::enable_if { cout< } typename std::enable_if { cout< } 可以看到std::enable_if的强大重载机制,即使返回值相同也可以重载,(NIMA当我瞎啊,入参类型不是不一样吗?引用到底影不影响啊?????) 下面来一段牛逼的代码: template string ToString(T t) { if(typeid(T) == typeid(int) || typeid(T) == typeid(double) || typeid(T) == typeid(float) ) { std::string stream ss; ss< return ss.str(); } else if(typeid(T) == typeid(string)) { return t; } } 用enable_if来替换: template typename std::enable_if ToString(T& t) { return std::to_string(t); } tempalte typename std::enable_if ToString(T& t) { return t; }
return f(arg);