## 使用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 #include #include 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 std::result_of(Args...)>::type ExcecuteFunc(const std::string& funcName, Args&&... args) { auto f = GetFunction(funcName); if (f == nullptr) { std::string s = "can not find this function " + funcName; throw std::exception(s.c_str()); } return f(std::forward(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 T* GetFunction(const std::string& funcName) { auto addr = GetProcAddress(m_hMod, funcName.c_str()); return (T*) (addr); } private: HMODULE m_hMod; std::map m_map; }; #endif //_DLLPARSER_HPP_ ``` ### 3.3.4 lambda链式调用 ``` #include #include #include template class Task; template class Task { public: Task(std::function&& f) : m_fn(std::move(f)){} Task(std::function& f) : m_fn(f){} template R Run(Args&&... args) { return m_fn(std::forward(args)...); } template auto Then(F& f)->Task::type(Args...)> { using return_type = typename std::result_of::type; auto func = std::move(m_fn); return Task([func, f](Args&&... args) {return f(func(std::forward(args)...)); }); } private: std::function m_fn; }; void TestTask() { Task 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; } ``` 后面若干章节较难,为了节约时间所以跳过了