Init
This commit is contained in:
494
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第一章.md
Normal file
494
02-Note/读书笔记/深入应用C++11代码优化与工程级应用/深入应用c++11代码优化与工程级应用第一章.md
Normal file
@@ -0,0 +1,494 @@
|
||||
## 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;//只获取了第三个值
|
||||
```
|
Reference in New Issue
Block a user