494 lines
13 KiB
Markdown
494 lines
13 KiB
Markdown
|
## 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 <class A>
|
|||
|
void func(void)
|
|||
|
{
|
|||
|
auto val = A::get();
|
|||
|
}
|
|||
|
|
|||
|
int main()
|
|||
|
{
|
|||
|
func<Foo>();
|
|||
|
func<Bar>();
|
|||
|
func<Bar2>();//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<<square_int(10)<<std::endl;
|
|||
|
std::cout<<square_int(10.1)<<std::endl;
|
|||
|
```
|
|||
|
为了保持函数的局部性,我们才选择的lambda,但C++11的lambda却导致多个类型时代码膨胀且重复,此时我们需要回过头来借助全局的模板了。
|
|||
|
|
|||
|
但C++ 14可以完美的解决上面的问题,因为C++14中lambda的参数可以用auto代替具体的类型:
|
|||
|
```
|
|||
|
auto square = [](auto x) { return x * x; };
|
|||
|
|
|||
|
std::cout<<square_int(10)<<std::endl;
|
|||
|
std::cout<<square_int(10.1)<<std::endl;
|
|||
|
```
|
|||
|
|
|||
|
## 1.1.2 decltype
|
|||
|
### decltype推导规则
|
|||
|
decltype(exp)
|
|||
|
1. exp是标识符、类访问表达式,decltype(type)和exp类型一样。
|
|||
|
2. exp是函数调用,decltype(exp)和返回值的类型一致。
|
|||
|
3. 其他情况,若exp是一个左值,则decltype(exp)是exp类型的左值引用,否则和exp类型一致。
|
|||
|
|
|||
|
```
|
|||
|
class Foo{
|
|||
|
static const int Number=0;
|
|||
|
int x;
|
|||
|
};
|
|||
|
|
|||
|
int n=0;
|
|||
|
volatile const int &x=n;
|
|||
|
|
|||
|
decltype(n) a=n; //a->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 <typename R,typename T,typename U>
|
|||
|
R add(T t,U u)
|
|||
|
{
|
|||
|
return t+u;
|
|||
|
}
|
|||
|
|
|||
|
int a=1;floatb=2.0;
|
|||
|
auto c=add<decltype(a+b)>(a+b);
|
|||
|
```
|
|||
|
|
|||
|
错误,定义返回值时参数变量还不存在
|
|||
|
```
|
|||
|
template <typename T,typename U>
|
|||
|
decltype(t+u) add(T t,U u)
|
|||
|
{
|
|||
|
return t+u;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
正确,但是如果T和U是无参构造函数的类就不行了
|
|||
|
```
|
|||
|
template <typename T,typename U>
|
|||
|
decltype(T()+U()) add(T t,U u)
|
|||
|
{
|
|||
|
return t+u;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
使用c++返回类型后置语法
|
|||
|
```
|
|||
|
template <typename T,typename U>
|
|||
|
auto add(T t,U u)->decltype(t+u)
|
|||
|
{
|
|||
|
return t+u;
|
|||
|
}
|
|||
|
```
|
|||
|
另一个例子,根据使用的重载函数推导返回类型
|
|||
|
```
|
|||
|
int& foo(int& i);
|
|||
|
float foo(float& f);
|
|||
|
|
|||
|
template<typename T>
|
|||
|
auto func(T& val)-> decltype(foo(val)
|
|||
|
{
|
|||
|
return foo(val);
|
|||
|
}
|
|||
|
```
|
|||
|
### 1.2.2 模板的别名
|
|||
|
using和typedef都可以定义一个新的类型别名,但是using可以直接定义一个模板别名,而不像typedef需要一个struct 外敷类来实现。而且using的可读性更高。
|
|||
|
```
|
|||
|
template <typename T>
|
|||
|
struct func_t{
|
|||
|
typedef void (*type)(t,t);
|
|||
|
};
|
|||
|
func_t<int>::type xx_1;
|
|||
|
|
|||
|
template<typename T>
|
|||
|
using func_t=void(*)(T,T);
|
|||
|
func_t<int> xx_2;
|
|||
|
```
|
|||
|
另外using可以定义任意类型的模板表达方式
|
|||
|
```
|
|||
|
template<typename T>
|
|||
|
using type_tt = T;
|
|||
|
type_tt<int> 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<int> list){
|
|||
|
|
|||
|
}
|
|||
|
};
|
|||
|
```
|
|||
|
之后就可以执行Foo foo={1,2,3,4,5,6,7};(需要自己实现list对类内部变量的赋值)
|
|||
|
|
|||
|
同时std::initializer_list可以用于传值
|
|||
|
```
|
|||
|
void func(std::initializer_list<int> l){
|
|||
|
for(auto it=l.begin();it!=l.end();++it)
|
|||
|
{std::cout<<*it<<std::endl;}
|
|||
|
}
|
|||
|
|
|||
|
int main()
|
|||
|
{
|
|||
|
func({});
|
|||
|
func({1,2,3})
|
|||
|
}
|
|||
|
```
|
|||
|
PS.std::initializer_list为了提高初始化效率,使用的是引用传值,所以当传值结束时,这些里面的变量也都被销毁了。所以如果需要传值,这需要传递,它初始化容器的值。
|
|||
|
|
|||
|
初始化列表同时可以用于检查类型收窄,即因为类型转换而导致的数据丢失。
|
|||
|
### 1.4.1基于范围的for循环
|
|||
|
当遍历需要修改容器中的值时,则需要使用引用:
|
|||
|
```
|
|||
|
for(auto& n: arr)
|
|||
|
{
|
|||
|
std::cout<<n++<<std::endl;
|
|||
|
}
|
|||
|
```
|
|||
|
如果只是希望遍历而不希望修改可以使用const auto& n;
|
|||
|
### 1.4.2基于范围的for循环使用细节
|
|||
|
在:后面可以写带返回遍历对象的函数,而且这个函数只执行一次。
|
|||
|
```
|
|||
|
#include <iostream>
|
|||
|
#include <vector>
|
|||
|
std::vector<int> arr={1,2,3,4,5,6};
|
|||
|
|
|||
|
std::vector<int>& get_range()
|
|||
|
{
|
|||
|
std::cout<<"get_range->"<<std::endl;
|
|||
|
return arr;
|
|||
|
}
|
|||
|
int main()
|
|||
|
{
|
|||
|
for(auto val: get_range())
|
|||
|
{
|
|||
|
std::cout<<val<<std::endl;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
```
|
|||
|
在使用基于范围的for循环时,如果遍历时修改容器大小,很可能出现因为迭代器失效而导致的异常情况。
|
|||
|
|
|||
|
### 1.4.3 让基于范围的for循环支持自定义类型
|
|||
|
```
|
|||
|
#include <iostream>
|
|||
|
|
|||
|
namespace detail_range {
|
|||
|
template <typename T>
|
|||
|
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 <typename T>
|
|||
|
class impl {
|
|||
|
public:
|
|||
|
using value_type = T;
|
|||
|
using reference = const value_type&;
|
|||
|
using const_reference = const value_type&;
|
|||
|
using iterator = const detail_range::iterator<value_type>;
|
|||
|
using const_iterator= const detail_range::iterator<value_type>;
|
|||
|
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<size_type>((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 <typename T>
|
|||
|
detail_range::impl<T> range(T end)
|
|||
|
{
|
|||
|
return{ {},end,1 };
|
|||
|
}
|
|||
|
|
|||
|
template <typename T>
|
|||
|
detail_range::impl<T> range(T begin, T end)
|
|||
|
{
|
|||
|
return{ begin,end,1 };
|
|||
|
}
|
|||
|
|
|||
|
template <typename T,typename U>
|
|||
|
auto range(T begin, T end, U step)->detail_range::impl<decltype(begin + step)>
|
|||
|
{
|
|||
|
using r_t = detail_range::impl<decltype(begin + step)>;
|
|||
|
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<<f()<<std::endl;
|
|||
|
```
|
|||
|
在以上lambda表示式中修改外部变量并不会真正影响到外部,我们却仍然无法修改他们。
|
|||
|
那么如果希望去修改按值捕获的外部变量就需要显式致命表达式为mutable:
|
|||
|
```
|
|||
|
int a=0;
|
|||
|
auto f1=[=]{return a++;}; //error
|
|||
|
auto f2=[=]()mutable{return a++;}; //ok,mutable
|
|||
|
```
|
|||
|
需要注意的是:被mutable修饰的lambda就算没有参数也需要写参数列表。
|
|||
|
|
|||
|
我们可以使用std::function与std::bind来操作lambda表达式,另外对于没有捕获任何变量的lambda表达式,还可以被转化为一个普通的函数指针:
|
|||
|
```
|
|||
|
using func_t=int(*)(int);
|
|||
|
func_t f=[](int a){return a;};
|
|||
|
f(123);
|
|||
|
```
|
|||
|
lambda表示式可以基本代替std::function(一些老式库不支持lambda)
|
|||
|
### 1.7 tupe元组
|
|||
|
```
|
|||
|
tuple<const char*,int>tp=make_tuple(sendPack,nSendSize);//构建一个tuple
|
|||
|
```
|
|||
|
使用tuple相当于构建一个对应的结构体<br>
|
|||
|
可以使用std::tie来进行解包来获取对应的数值,也可以用来创建元组。
|
|||
|
```
|
|||
|
int x=1;
|
|||
|
int y=2;
|
|||
|
string s="aa";
|
|||
|
auto tp=std::tie(x,s,y);
|
|||
|
//tp是实际类型是std::tuple<int&,string&,int&>
|
|||
|
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;//只获取了第三个值
|
|||
|
```
|