508 lines
11 KiB
Markdown
508 lines
11 KiB
Markdown
|
## 使用c++11消除重复,提高代码质量
|
|||
|
### 3.1 type_traits———类型萃取
|
|||
|
type_traits提供了丰富的编译期计算、查询、判断、转换和选择的帮助类,在很多场合都会使用到。
|
|||
|
### 3.1.1 基本的type_traits
|
|||
|
##### 1.基本的type_traits<br>
|
|||
|
无需自己定义static const int或者enum类型,只需要从std::integral_constant派生:
|
|||
|
```
|
|||
|
template<typename Type>
|
|||
|
struct GetLeftSize : std::integral_constant<int,1>
|
|||
|
```
|
|||
|
之后用GetLeftSize::value来取值。
|
|||
|
##### 2.类型判断的type_traits
|
|||
|
这些从std::integral_constant派生的type_traits,可以通过std::is_xxxx来判断T的类型。也可以通过std::is_xxxx::value是否为true来判断模板类型是否为目标类型。
|
|||
|
```
|
|||
|
#include <iostream>
|
|||
|
#include <type_traits>
|
|||
|
|
|||
|
int main(){
|
|||
|
std::cout<<"is_const:"<<std::endl;
|
|||
|
std::cout<<"int:"<<std::is_const<int>::value<<std::endl;
|
|||
|
std::cout<<"const int:"<<std::is_const<const int>::value<<std::endl;
|
|||
|
std::cout<<"const int&:"<<std::is_const<const int&>::value<<std::endl;
|
|||
|
std::cout<<"const int*:"<<std::is_const<const int*>::value<<std::endl;
|
|||
|
std::cout<<"int* const:"<<std::is_const<int* const>::value<<std::endl;
|
|||
|
}
|
|||
|
```
|
|||
|
##### 3.判断两类型之间的关系traits
|
|||
|
```
|
|||
|
struct is_same;//判断两类型是否相同
|
|||
|
struct is_base_of;//判断一个是否为另一个的基类
|
|||
|
struct is_convertible;//判断模板参数类型能否转化为后面的模板参数类型
|
|||
|
```
|
|||
|
##### 4.类型转换traits
|
|||
|
常用的类型的转换traits包括对const的修改————const的移除与添加,引用的修改————引用的移除与添加,数组的修改和指针的修改。<br>
|
|||
|
创建对象时需要去除引用:
|
|||
|
```
|
|||
|
template<typename T>
|
|||
|
typnename std::remove_reference<t>::type* Create()
|
|||
|
{
|
|||
|
typedef typename std::remove_reference<t>::type U;
|
|||
|
return new U();
|
|||
|
}
|
|||
|
```
|
|||
|
模板参数T可能是引用类型,而创建对象时,需要原始的类型,不能用引用类型,所以需要将可能的引用引用。同样const也需要考虑进去。<br>
|
|||
|
为了简洁可以使用std::decay,移除const与引用。<br>
|
|||
|
另一个用处就是用来保存函数指针。
|
|||
|
```
|
|||
|
{
|
|||
|
using FnType=typename std::decay<F>::type;
|
|||
|
SimpFunction(F& f):m_fn(f){
|
|||
|
|
|||
|
}
|
|||
|
void run()
|
|||
|
{
|
|||
|
m_fn();
|
|||
|
}
|
|||
|
FnType m_fn;
|
|||
|
|
|||
|
}
|
|||
|
```
|
|||
|
### 3.1.2根据条件选择traits
|
|||
|
```
|
|||
|
template<bool B,class T,class F>
|
|||
|
struct conditional;
|
|||
|
|
|||
|
typedef std::conditional<true,int,float>::type A;//int
|
|||
|
```
|
|||
|
### 3.1.3获取可调用对象返回类型的traits
|
|||
|
一些模板函数的返回值不能直接确定,虽然可以通过decltype来进行推断,但是可读性较差。<br>
|
|||
|
可以通过auto -> decltype进行和简化。
|
|||
|
```
|
|||
|
template<typename F,template Arg>
|
|||
|
auto Func(F f,Arg arg)->decltype(f(arg))
|
|||
|
{
|
|||
|
return f(arg);
|
|||
|
}
|
|||
|
```
|
|||
|
不过在类没有构造函数的情况下就不行了。此时如果希望编译器进行推导,就需要使用std::declval。
|
|||
|
```
|
|||
|
decltype(std::declval<int>(std::declval<int>())) i=4;
|
|||
|
```
|
|||
|
declval获得的临时值引用不能用于求值,因此需要使用decaltype来推断出最终值。<br>
|
|||
|
当然可以用更加简洁的方法:
|
|||
|
```
|
|||
|
std::result_of<A(int)>::type i=4;
|
|||
|
```
|
|||
|
### 3.1.4 根据条件禁用或者启用某种或者某些类型traits
|
|||
|
```
|
|||
|
template<class T>
|
|||
|
typnename std::enable_if<std::is_arithmetic<T>::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<class... T>
|
|||
|
void f(T... args)
|
|||
|
{
|
|||
|
std::cout<<sizeof...(args)<<std::endl;
|
|||
|
}
|
|||
|
|
|||
|
f(); //0
|
|||
|
f(1,2); //2
|
|||
|
f(1,2.5,""); //3
|
|||
|
```
|
|||
|
1. 递归函数方式展开参数包
|
|||
|
```
|
|||
|
void print()
|
|||
|
{
|
|||
|
std::cout << "empty" << std::endl;
|
|||
|
}
|
|||
|
|
|||
|
template<class T, class... Args>
|
|||
|
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<typename T,typename T1>
|
|||
|
void print(T t,T1, t1)
|
|||
|
{
|
|||
|
std::cout<<t<<""<<t1<<std::endl;
|
|||
|
}
|
|||
|
```
|
|||
|
2. 逗号表达式和初始化列表方式展开参数包
|
|||
|
|
|||
|
逗号运算符:<br>
|
|||
|
运算符有左右两个表达式,先计算左侧的,之后丢弃结果再计算右侧。
|
|||
|
```
|
|||
|
d=(a=b,c);
|
|||
|
```
|
|||
|
b会先赋值给a,接着括号中的逗号表达式将会返回c的值。
|
|||
|
```
|
|||
|
template <class T>
|
|||
|
void printarg(T t)
|
|||
|
{
|
|||
|
std::cout<<t<<std::endl;
|
|||
|
}
|
|||
|
|
|||
|
template <class ...Args>
|
|||
|
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),}<br>
|
|||
|
之后也可以使用std::initializer_list来优化,这样就无需定义一个数组了,使用lambda的话printarg函数就无需定义了。
|
|||
|
### 3.2.2可变参数模板
|
|||
|
std::tuple是一个可变参数模型
|
|||
|
```
|
|||
|
template<class... Type>
|
|||
|
class tuple;
|
|||
|
```
|
|||
|
1. 模板递归和特化方式展开参数包
|
|||
|
```
|
|||
|
//前向声明
|
|||
|
template<typename... Args>
|
|||
|
struct Sum;
|
|||
|
|
|||
|
//基本定义
|
|||
|
template<typename First,typne... Rest>
|
|||
|
struct Sum<First.Rest...>
|
|||
|
{
|
|||
|
enum{value=Sum<First>::value+Sum::<Rest...>::value};
|
|||
|
};
|
|||
|
|
|||
|
//递归终止
|
|||
|
template<typename Last>
|
|||
|
struct Sum<Last>
|
|||
|
{
|
|||
|
enum{value=sizeof(Last)};
|
|||
|
};
|
|||
|
```
|
|||
|
2. 继承方式展开参数包
|
|||
|
有点复杂不做笔记了<br>
|
|||
|
除了用继承还可以使用using来实现。
|
|||
|
```
|
|||
|
template<int N,int... Indexes>
|
|||
|
struct MakeIndexes{
|
|||
|
using type=MakeIndexes<N-1,N-1,Indexes...>::type
|
|||
|
};
|
|||
|
|
|||
|
template<int... Indexes>
|
|||
|
struct MakeIndexes<0,Indexes...>
|
|||
|
{
|
|||
|
using type=IndexSeq<Indexes...>;
|
|||
|
};
|
|||
|
|
|||
|
MakeIndexes<3>=>IndexSeq<0,1,2>
|
|||
|
```
|
|||
|
### 3.2.3可变参数模板
|
|||
|
免去手写大量的模板类代码,而让编译器来自动实现。
|
|||
|
```
|
|||
|
template<typename... Args>
|
|||
|
T* Instance(Args... args)
|
|||
|
{
|
|||
|
return new T(args);
|
|||
|
}
|
|||
|
|
|||
|
A* pa=Instance<A>(1);
|
|||
|
B* pb=Instance<B>(1,2);
|
|||
|
```
|
|||
|
上面代码中的Args是拷贝值,使用右值引用与完美转发来消除损耗。
|
|||
|
```
|
|||
|
template<typename... Args>
|
|||
|
T* Instance(Args&&... args)
|
|||
|
{
|
|||
|
return new T(std::forward<Args>(args)...);
|
|||
|
}
|
|||
|
```
|
|||
|
### 3.3 可变参数模板和type_taits的综合应有
|
|||
|
#### 3.3.1 optional的实现
|
|||
|
使用placement new的时候容易出现内存对齐的问题,可以使用:
|
|||
|
```
|
|||
|
#include <iostream>
|
|||
|
#include <type_traits>
|
|||
|
|
|||
|
struct A
|
|||
|
{
|
|||
|
int avg;
|
|||
|
A(int a,int b):avg((a+b)/2){}
|
|||
|
};
|
|||
|
|
|||
|
typedef std::aligned_storage<sizeof(A),alignof(A)>::type Aligned_A;
|
|||
|
|
|||
|
int main()
|
|||
|
{
|
|||
|
Aligned_A a,b;
|
|||
|
new (&a) A(10,20);
|
|||
|
b=a;
|
|||
|
std::cout<<reinterpret_cast<A&>(b).avg<<std::endl;
|
|||
|
return0;
|
|||
|
}
|
|||
|
```
|
|||
|
#### 3.3.2 惰性求值类Lazy的实现
|
|||
|
```
|
|||
|
#ifndef _LAZY_HPP_
|
|||
|
#define _LAZY_HPP_
|
|||
|
|
|||
|
#include <boost/optional.hpp>
|
|||
|
template<typename T>
|
|||
|
struct Lazy
|
|||
|
{
|
|||
|
Lazy(){}
|
|||
|
|
|||
|
template <typename Func, typename... Args>
|
|||
|
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<T()> m_func;
|
|||
|
boost::optional<T> m_value;
|
|||
|
};
|
|||
|
|
|||
|
template<class Func, typename... Args>
|
|||
|
Lazy<typename std::result_of<Func(Args...)>::type>
|
|||
|
lazy(Func && fun, Args && ... args)
|
|||
|
{
|
|||
|
return Lazy<typename std::result_of<Func(Args...)>::type>(
|
|||
|
std::forward<Func>(fun), std::forward<Args>(args)...);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#endif
|
|||
|
```
|
|||
|
```
|
|||
|
#include "Lazy.hpp"
|
|||
|
|
|||
|
#include <iostream>
|
|||
|
#include <memory>
|
|||
|
|
|||
|
struct BigObject
|
|||
|
{
|
|||
|
BigObject()
|
|||
|
{
|
|||
|
std::cout << "lazy load big object" << std::endl;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
struct MyStruct
|
|||
|
{
|
|||
|
MyStruct()
|
|||
|
{
|
|||
|
m_obj = lazy([]{return std::make_shared<BigObject>(); });
|
|||
|
}
|
|||
|
|
|||
|
void Load()
|
|||
|
{
|
|||
|
m_obj.Value();
|
|||
|
}
|
|||
|
|
|||
|
Lazy<std::shared_ptr<BigObject>> 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<int> 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 <Windows.h>
|
|||
|
#include <string>
|
|||
|
#include <map>
|
|||
|
#include <functional>
|
|||
|
#include <iostream>
|
|||
|
|
|||
|
|
|||
|
class DllParser
|
|||
|
{
|
|||
|
public:
|
|||
|
|
|||
|
DllParser()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
~DllParser()
|
|||
|
{
|
|||
|
UnLoad();
|
|||
|
}
|
|||
|
|
|||
|
bool Load(const std::string& dllPath)
|
|||
|
{
|
|||
|
m_hMod = LoadLibrary(dllPath.data());
|
|||
|
if (nullptr == m_hMod)
|
|||
|
{
|
|||
|
std::cout << "LoadLibrary failed\n";
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
template <typename T, typename... Args>
|
|||
|
typename std::result_of<std::function<T>(Args...)>::type
|
|||
|
ExcecuteFunc(const std::string& funcName, Args&&... args)
|
|||
|
{
|
|||
|
auto f = GetFunction<T>(funcName);
|
|||
|
|
|||
|
if (f == nullptr)
|
|||
|
{
|
|||
|
std::string s = "can not find this function " + funcName;
|
|||
|
throw std::exception(s.c_str());
|
|||
|
}
|
|||
|
|
|||
|
return f(std::forward<Args>(args)...);
|
|||
|
}
|
|||
|
|
|||
|
private:
|
|||
|
bool UnLoad()
|
|||
|
{
|
|||
|
if (m_hMod == nullptr)
|
|||
|
return true;
|
|||
|
|
|||
|
auto b = FreeLibrary(m_hMod);
|
|||
|
if (!b)
|
|||
|
return false;
|
|||
|
|
|||
|
m_hMod = nullptr;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
template <typename T>
|
|||
|
T* GetFunction(const std::string& funcName)
|
|||
|
{
|
|||
|
auto addr = GetProcAddress(m_hMod, funcName.c_str());
|
|||
|
return (T*) (addr);
|
|||
|
}
|
|||
|
|
|||
|
private:
|
|||
|
HMODULE m_hMod;
|
|||
|
std::map<std::string, FARPROC> m_map;
|
|||
|
};
|
|||
|
|
|||
|
#endif //_DLLPARSER_HPP_
|
|||
|
```
|
|||
|
### 3.3.4 lambda链式调用
|
|||
|
```
|
|||
|
#include <iostream>
|
|||
|
#include <functional>
|
|||
|
#include <type_traits>
|
|||
|
|
|||
|
template<typename T>
|
|||
|
class Task;
|
|||
|
|
|||
|
template<typename R, typename...Args>
|
|||
|
class Task<R(Args...)>
|
|||
|
{
|
|||
|
public:
|
|||
|
Task(std::function<R(Args...)>&& f) : m_fn(std::move(f)){}
|
|||
|
|
|||
|
Task(std::function<R(Args...)>& f) : m_fn(f){}
|
|||
|
|
|||
|
template<typename... Args>
|
|||
|
R Run(Args&&... args)
|
|||
|
{
|
|||
|
return m_fn(std::forward<Args>(args)...);
|
|||
|
}
|
|||
|
|
|||
|
template<typename F>
|
|||
|
auto Then(F& f)->Task<typename std::result_of<F(R)>::type(Args...)>
|
|||
|
{
|
|||
|
using return_type = typename std::result_of<F(R)>::type;
|
|||
|
|
|||
|
auto func = std::move(m_fn);
|
|||
|
|
|||
|
return Task<return_type(Args...)>([func, f](Args&&... args)
|
|||
|
{return f(func(std::forward<Args>(args)...)); });
|
|||
|
}
|
|||
|
|
|||
|
private:
|
|||
|
std::function<R(Args...)> m_fn;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
void TestTask()
|
|||
|
{
|
|||
|
Task<int(int)> task = [](int i){ return i; };
|
|||
|
auto result = task.Then([](int i){return i + 1; }).Then([](int i){
|
|||
|
return i + 2; }).Then([](int i){return i + 3; }).Run(1);
|
|||
|
std::cout << result << std::endl; // 7
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int main(void)
|
|||
|
{
|
|||
|
TestTask();
|
|||
|
|
|||
|
system("pause");
|
|||
|
return 0;
|
|||
|
}
|
|||
|
```
|
|||
|
后面若干章节较难,为了节约时间所以跳过了
|