## 1.1.1 auto笔记 ### auto与引用作用则不会抛弃const ``` int x=0; const auto& g=x; //g->const int& auto& h =g; //h->const int& ``` ### 什么时候用auto 比如在使用迭代器遍历的时候,对类型及获取并不关心的时候。 ### auto推断模板函数对应函数返回值 ``` struct Foo { static int get(void) { return 0; } }; struct Bar { static const char* get(void) { return "0"; }; }; struct Bar2 { }; template void func(void) { auto val = A::get(); } int main() { func(); func(); func();//Bar2类中不包含get方法,此时编译器会报错 system("pause"); return 0; } ``` ### c++14 增加了函数返回值推导 在C++11下,如果你想要打印出一个数的平方,可能需要这样: ``` auto square_int = [](int x) { return x * x; }; auto square_double = [](double x) { return x * x; }; std::cout<int decltype(x) b=n; //b->const volatile int & decltype(Foo::Number) c=0; //c->const int Foo foo; decltype(foo.x) d=0; //d->int ``` ``` int& func_int_r(void); int&& func_int_rr(void); int func_int(void); const int& func_cint_r(void); const int&& func_cint_rr(void); const int func_cint(void); const Foo func_cfoo(void); int x=0; decltype(func_int_r()) a1=x; //a1->int & decltype(func_int_rr()) b1=x; //b1->int && decltype(func_int()) c1=x; //c1->int decltype(func_cint_r()) a2=x; //a2->const int & decltype(func_cint_r()) b2=x; //b2->const int && decltype(func_cint()) c2=x; //c2->int & 因为是纯右值,所以舍弃掉cv符,只有类类型可以携带 decltype(func_cfoo()) ff=foo(); //ff->const foo ``` ``` struct Foo{int x;}; const Foo foo=Foo(); decltype(foo.x) a=0; //a->int decltype((foo.x)) b=a; //b->const int & int n=0,m=0; decltype(n+m) c=; c=0; //c->int decltype(n+=m) d=c; d=c; //d->int & ``` a和b的结果:仅仅多加了一对括号,他们得到的类型却是不同的。具体解释请见P12 ++++ ### decltype实际应用 具体解释请见P12 ## 1.1.3 返回类型后置语法——auto和decltype结合使用 如何实现类似: ``` template R add(T t,U u) { return t+u; } int a=1;floatb=2.0; auto c=add(a+b); ``` 错误,定义返回值时参数变量还不存在 ``` template decltype(t+u) add(T t,U u) { return t+u; } ``` 正确,但是如果T和U是无参构造函数的类就不行了 ``` template decltype(T()+U()) add(T t,U u) { return t+u; } ``` 使用c++返回类型后置语法 ``` template auto add(T t,U u)->decltype(t+u) { return t+u; } ``` 另一个例子,根据使用的重载函数推导返回类型 ``` int& foo(int& i); float foo(float& f); template auto func(T& val)-> decltype(foo(val) { return foo(val); } ``` ### 1.2.2 模板的别名 using和typedef都可以定义一个新的类型别名,但是using可以直接定义一个模板别名,而不像typedef需要一个struct 外敷类来实现。而且using的可读性更高。 ``` template struct func_t{ typedef void (*type)(t,t); }; func_t::type xx_1; template using func_t=void(*)(T,T); func_t xx_2; ``` 另外using可以定义任意类型的模板表达方式 ``` template using type_tt = T; type_tt a; ``` ### 1.3.2初始化列表的使用细节 初始化列表可以用于对应类型函数的返回值中。 例如:就等于Foo(123,321.0),(假设有Foo(int,float)这个构造函数) ``` Foo function(void) { return {123,321.0}; } ``` ### 1.3.3初始化列表 自己定义的类并没有类似Vector、Map容器的任意长度的初始化功能,如果想要实现可以添加一下构造函数来实现。 ``` class Foo { public: Foo(std::initializer_list list){ } }; ``` 之后就可以执行Foo foo={1,2,3,4,5,6,7};(需要自己实现list对类内部变量的赋值) 同时std::initializer_list可以用于传值 ``` void func(std::initializer_list l){ for(auto it=l.begin();it!=l.end();++it) {std::cout<<*it< #include std::vector arr={1,2,3,4,5,6}; std::vector& get_range() { std::cout<<"get_range->"< namespace detail_range { template class iterator { public: using value_type = T; using size_type = size_t; private: size_type cursor_; const value_type step_; value_type value_; public: iterator(size_type cur_start, value_type begin_val, value_type step_val) :cursor_(cur_start), step_(step_val), value_(begin_val) { value_ += (step_*cursor_); } value_type operator*() const { return value_; } bool operator!=(const iterator& rhs) const { return (cursor_!=rhs.cursor_); } iterator& operator++(void) { value_ += step_; ++cursor_; return (*this); } }; template class impl { public: using value_type = T; using reference = const value_type&; using const_reference = const value_type&; using iterator = const detail_range::iterator; using const_iterator= const detail_range::iterator; using size_type = typename iterator::size_type;//这里的typename是告诉编译器后面跟着的是类型 private: const value_type begin_; const value_type end_; const value_type step_; const size_type max_count_; size_type get_adjusted_count(void) const { if (step_ > 0 && begin_ >= end_) throw std::logic_error("End value must be greater than begin value."); else if (step_ < 0 && begin_ <= end_) throw std:: logic_error("End value must be less than begin value."); size_type x = static_cast((end_ - begin_) / step_); if (begin_ + (step_*x) != end_) ++x;//为了防止出现类似{0,12,0.5}之类的迭代要求,而做的升位处理 return x; } public: impl(value_type begin_val, value_type end_val, value_type step_val) :begin_(begin_val), end_(end_val), step_(step_val), max_count_(get_adjusted_count()) {} size_type size(void) const { return max_count_; } const_iterator begin(void) const { return{ 0,begin_,step_ }; } const_iterator end(void) const { return{ max_count_,begin_,step_ }; } }; template detail_range::impl range(T end) { return{ {},end,1 }; } template detail_range::impl range(T begin, T end) { return{ begin,end,1 }; } template auto range(T begin, T end, U step)->detail_range::impl { using r_t = detail_range::impl; return r_t(begin, end, step); } } int main() { for (int i : detail_range::range(15)) { std::cout << " " << i; } std::cout << std::endl; for (auto i : detail_range::range(2,8,0.5)) { std::cout << " " << i; } std::cout << std::endl; system("pause"); return 0; } ``` ### 1.5.2 可调用对象包装器———std::function 用于解决以下可调用对象的统一操作的问题 1. 函数指针 2. 具有operator() 成员函数的类对象(仿函数) 3. 可被转化为函数指针的类对象 4. 类成员(函数)指针 std::function也可以作为函数参数来使用,也就是相当于回调函数的作用 ### 1.5.3 std::bind绑定器 std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以用std::function进行保存,并延迟调用到任何我们需要调用的时候。 作用: 1. 将可调用对象与其他参数一起绑定成一个仿函数 2. 将多元可调用对象转成一元或者(n-1)元可调用对象,即只绑定部分参数。 关于bind的使用: bind(&要调用的函数,&对象, 要调用函数的参数1,要调用函数的参数2...,_1(bind函数的参数1),_2(bind函数的参数2)...) 注:如果bind的是一个非静态成员函数,第二个参数一定是一个该成员的一个指针,后面才是正常的参数。 数this指针作为一个隐含参数传递给非静态成员函数,用以指向该成员函数所属类所定义的对象。当不同对象调用同一个类的成员函数代码时,编译器会依据该成员函数的this指针所指向的不同对象来确定应该一用哪个对象的数据成员。下面举一个简单例子。 (1)bind(&f)() 假设f是一个全局函数,绑定全局函数并调用; (2)bind (&A::f, A())() 假设A是一个构造函数为空的类,这个形式绑定了类的成员函数,故第二个参数需要传入一个成员(成员静态函数除外); ### 1.6 lambda表达式 ``` [capture](params)opt->ret{body;}; auto f=[](int a)->int {return a+1;}; ``` c++11允许省略lambada表达式的返回值定义: ``` auto x1=[](int i) {return i;}; auto x2=[](){return {1,2};); //error:初始化列表无法推断 ``` 在没有参数列表时,()是可以省略的 ``` auto f1=[]{return 1;}; ``` ``` class A{ public: int i_=0; void func(int x,int y) { auto x1=[]{return i_;}; //error,没有捕获外部变量 auto x2=[=]{return i_+x+y;}; //ok auto x3=[&]{return i_+x+y;}; //ok auto x4=[this]{return i_;}; //ok auto x5=[this]{return i_+x+y;}; //error,没有捕获x与y auto x6=[this,x,y]{return i_+x+y;};//Ok auto x7=[this]{return i_++;}; //ok } }; int a=0,b=1; auto f1=[]{return a;}; //error,没有捕获外部变量 auto f2=[&]{return a++;}; //ok auto f3=[=]{return a;}; //ok auto f4=[=]{return a++;}; //error, a是以复制方式捕获的,无法修改 auto f5=[a]{return a+b;}; //error,没有捕获b变量 auto f6=[a,&b]{return a+(b++);};//ok auto f7=[=,&b]{return a+(b++);};//ok ``` 需要注意的是,默认状态下lambda表达式无法修改通过复制方式捕获的外部变量,如果希望修改,需啊需要使用引用方式来捕获。 ``` int a=0; auto f=[=]{return a;}; a+=1; std::cout<tp=make_tuple(sendPack,nSendSize);//构建一个tuple ``` 使用tuple相当于构建一个对应的结构体
可以使用std::tie来进行解包来获取对应的数值,也可以用来创建元组。 ``` int x=1; int y=2; string s="aa"; auto tp=std::tie(x,s,y); //tp是实际类型是std::tuple const char* data=tp.get<0>(); int len=tp.get<1>(); int x,y; string a; std::tie(x,a,y)=tp; ``` 你可以使用std::ignore占位符来表示不解某个位置的值。 ``` std::tie(std::ignore,std::ignore,y)=tp;//只获取了第三个值 ```