## 使用c++11消除重复,提高代码质量
### 3.1 type_traits———类型萃取
type_traits提供了丰富的编译期计算、查询、判断、转换和选择的帮助类,在很多场合都会使用到。
### 3.1.1 基本的type_traits
##### 1.基本的type_traits
无需自己定义static const int或者enum类型,只需要从std::integral_constant派生:
```
template
struct GetLeftSize : std::integral_constant
```
之后用GetLeftSize::value来取值。
##### 2.类型判断的type_traits
这些从std::integral_constant派生的type_traits,可以通过std::is_xxxx来判断T的类型。也可以通过std::is_xxxx::value是否为true来判断模板类型是否为目标类型。
```
#include
#include
int main(){
std::cout<<"is_const:"<::value<::value<::value<::value<::value<
创建对象时需要去除引用:
```
template
typnename std::remove_reference::type* Create()
{
typedef typename std::remove_reference::type U;
return new U();
}
```
模板参数T可能是引用类型,而创建对象时,需要原始的类型,不能用引用类型,所以需要将可能的引用引用。同样const也需要考虑进去。
为了简洁可以使用std::decay,移除const与引用。
另一个用处就是用来保存函数指针。
```
{
using FnType=typename std::decay::type;
SimpFunction(F& f):m_fn(f){
}
void run()
{
m_fn();
}
FnType m_fn;
}
```
### 3.1.2根据条件选择traits
```
template
struct conditional;
typedef std::conditional::type A;//int
```
### 3.1.3获取可调用对象返回类型的traits
一些模板函数的返回值不能直接确定,虽然可以通过decltype来进行推断,但是可读性较差。
可以通过auto -> decltype进行和简化。
```
template
auto Func(F f,Arg arg)->decltype(f(arg))
{
return f(arg);
}
```
不过在类没有构造函数的情况下就不行了。此时如果希望编译器进行推导,就需要使用std::declval。
```
decltype(std::declval(std::declval())) i=4;
```
declval获得的临时值引用不能用于求值,因此需要使用decaltype来推断出最终值。
当然可以用更加简洁的方法:
```
std::result_of::type i=4;
```
### 3.1.4 根据条件禁用或者启用某种或者某些类型traits
```
template
typnename std::enable_if::value,T>::type foo(T t)
{
return t;
}
auto r=foo(1);
auto r1=foo(1.2);
auto r2=foo("test");//error
```
不但可以作用于返回值,也可以作用于模板定义、类模板特化和入参类型的限定。
## 3.2 可变参数模板
在c++11中允许模板定义中包含0到任意个数模板参数。声明可变参数模板需要在typename或class后面带上省略号
省略号作用有两个:
1. 声明一个参数包,这个参数包中可以包含0到任意个模板参数。
2. 在模板定义的右边,可以将参数包展开成一个一个独立参数。
### 3.2.1 可变参数模板函数
```
template
void f(T... args)
{
std::cout<
void print(T head, Args... rest)
{
std::cout << "parameter" << head << std::endl;
print(rest...);
}
int main()
{
print(1,2,3,4,5);
return 0;
}
```
当然递归终止函数也可以写成,或者使用更多参数
```
template
void print(T t,T1, t1)
{
std::cout<
运算符有左右两个表达式,先计算左侧的,之后丢弃结果再计算右侧。
```
d=(a=b,c);
```
b会先赋值给a,接着括号中的逗号表达式将会返回c的值。
```
template
void printarg(T t)
{
std::cout<
void expand(Args... args)
{
int arr[]={(printarg(args),0)...};
}
expand(1,2,3,4);
```
这里使用了c++11的初始化列表特性,{(printarg(args),0)...}将会展开成{(printarg(args1),0),(printarg(args2),0),(printarg(args3),0),(printarg(args4),0),}
之后也可以使用std::initializer_list来优化,这样就无需定义一个数组了,使用lambda的话printarg函数就无需定义了。
### 3.2.2可变参数模板
std::tuple是一个可变参数模型
```
template
class tuple;
```
1. 模板递归和特化方式展开参数包
```
//前向声明
template
struct Sum;
//基本定义
template
struct Sum
{
enum{value=Sum::value+Sum::::value};
};
//递归终止
template
struct Sum
{
enum{value=sizeof(Last)};
};
```
2. 继承方式展开参数包
有点复杂不做笔记了
除了用继承还可以使用using来实现。
```
template
struct MakeIndexes{
using type=MakeIndexes::type
};
template
struct MakeIndexes<0,Indexes...>
{
using type=IndexSeq;
};
MakeIndexes<3>=>IndexSeq<0,1,2>
```
### 3.2.3可变参数模板
免去手写大量的模板类代码,而让编译器来自动实现。
```
template
T* Instance(Args... args)
{
return new T(args);
}
A* pa=Instance(1);
B* pb=Instance(1,2);
```
上面代码中的Args是拷贝值,使用右值引用与完美转发来消除损耗。
```
template
T* Instance(Args&&... args)
{
return new T(std::forward(args)...);
}
```
### 3.3 可变参数模板和type_taits的综合应有
#### 3.3.1 optional的实现
使用placement new的时候容易出现内存对齐的问题,可以使用:
```
#include
#include
struct A
{
int avg;
A(int a,int b):avg((a+b)/2){}
};
typedef std::aligned_storage::type Aligned_A;
int main()
{
Aligned_A a,b;
new (&a) A(10,20);
b=a;
std::cout<(b).avg<
template
struct Lazy
{
Lazy(){}
template
Lazy(Func& f, Args && ... args)
{
m_func = [&f, &args...]{return f(args...); };
}
T& Value()
{
if (!m_value.is_initialized())
{
m_value = m_func();
}
return *m_value;
}
bool IsValueCreated() const
{
return m_value.is_initialized();
}
private:
std::function m_func;
boost::optional m_value;
};
template
Lazy::type>
lazy(Func && fun, Args && ... args)
{
return Lazy::type>(
std::forward(fun), std::forward(args)...);
}
#endif
```
```
#include "Lazy.hpp"
#include
#include
struct BigObject
{
BigObject()
{
std::cout << "lazy load big object" << std::endl;
}
};
struct MyStruct
{
MyStruct()
{
m_obj = lazy([]{return std::make_shared(); });
}
void Load()
{
m_obj.Value();
}
Lazy> m_obj;
};
int Foo(int x)
{
return x * 2;
}
void TestLazy()
{
//带参数的普通函数
int y = 4;
auto lazyer1 = lazy(Foo, y);
std::cout << lazyer1.Value() << std::endl;
//不带参数的lamda
Lazy lazyer2 = lazy([]{return 12; });
std::cout << lazyer2.Value() << std::endl;
//带参数的fucntion
std::function < int(int) > f = [](int x){return x + 3; };
auto lazyer3 = lazy(f, 3);
std::cout << lazyer3.Value() << std::endl;
//延迟加载大对象
MyStruct t;
t.Load();
}
int main(void)
{
TestLazy();
system("pause");
return 0;
}
```
#### 3.3.3 dll帮助类实现
首先需要封装GetProcAddress函数,需要解决:
1. 函数的返回值可能是不同类型,如果以一种通用的返回值来消除这种不同返回值导致的差异
2. 函数的入参数目可能任意个数,且类型也不尽相同,如何来消除入参个数和类型的差异
```
#ifndef _DLLPARSER_HPP_
#define _DLLPARSER_HPP_
#include
#include
#include