文章目录
1 auto关键字
使用auto声明的变量必须马上初始化,以让编译器推断出他的实际类型,在编译时期将auto占位符替换为真的类型
auto x = 10;
const auto *v = &x;
static auto y = 0.0f;
使用方式
注意事项:
- auto不能用于函数参数
- 不能用于非静态成员变量
- auto使用时机:
stl的迭代器
std::unordered_multimap<int, int> resultmap;
std::pair<std::unordered_multimap<int, int>::iterator , std::unordered_multimap<int, int>::iterator> range = resultmap.equal_range(key);
使用auto类型推导可以直接改成
std::unordered_multimap<int, int> resultmap;
auto range = resultmap.equal_range(key);
2 decltype关键字
decltype用于编译时推导出一个表达式的类型,语法格式如下:
decltype(exp) // exp表示一个表达式
举例子:
int x = 0;
decltype(x) y = 1; // y->int
decltype(x+y) z = 2; // z->int
decltype推导规则
- exp是标识符、类访问表达式:返回类型和exp一致
- exp是函数调用:返回类型和函数返回值的类型一致
- exp是左值:返回类型是exp的一个左值引用
- 其他情况:返回类型就是exp类型
decltype的实际应用:
template<typename t>
class a
{
private :
decltype(t.begin()) m_it;
//typename t::iterator m_it; //这种用法会出错
public:
void func(t& container)
{
m_it=container.begin();
}
};
返回类型后置语法
C++11增加返回类型后置语法,将decltype和auto结合起来使用
template <typename T, typename U>
auto add(T t,U u)->decltype(t + u)
{
return t + u;
}
3 模板的别名
C++中可以使用typedef重定义一个类型:typedef unsigned int uint_t; 但是不支持重定义函数模板
C++11新增重定义模板别名语法
template <typename val>
using str_map_t = std::map<std::string, Val>;
str_map_t<int> map1;
C++11的using语法和typedef一样,不会创造新类型,只是写法不一样
4 列表初始化
C++11中的列表初始化可以用于任何类型的对象,不仅限于之前的普通数组和POD类型。
class C
{
public:
C(int){};
private:
C(const C&);
}
int main()
{
C A{123};
C B = {123};// 列表初始化,私有拷贝构造不会影响
int a = {3};
int a {3};
}
任意长度的初始化列表
为类添加一个std::initializar_list构造函数,就可以拥有任意长度的初始化能力
class C
{
public:
C(std::initializer_list<int>){}
}
C c = {1, 2, 3, 4, 5}; // OK
5 基于范围的for循环遍历
auto自动推导出的是容器的value_type而不是迭代器
std::vector<int> arr {1, 2, 3};
for(auto & a: arr) // 去掉&代表拷贝遍历,有&代表可以修改,加上const减少拷贝
{
a++;
}
注意:
- set内部元素不能改变,set < int >使用auto&a 会自动转换const int &,在遍历内部修改值会报错
- 遍历时修改容器可能引起迭代器失效,基于范围的for循环倾向于在循环开始前确定好迭代范围,而不是每次迭代之前都去调用一次arr.end()
6 std::function 和bind绑定器
std::function
C++中函数指针的用途非常广泛,例如回调函数,接口类的设计等,但它只能指向全局或静态函数,无法用在类成员函数、lambda表达式或其他可调用对象,所以可以理解std::function是高级版的函数指针,作用范围更广。
可调用对象定义:
- 函数指针
- 具有operator()成员函数的类对象(仿函数)
- 可被转换为函数指针的类对象
- 类成员(函数)指针
使用代码示例:
typedef std::function<void ()> PrintFinFunction;
void print(const char *text, PrintFinFunction callback)
{
printf("%s\n", text);
if (callback)
callback();
}
// 普通函数
void printFinCallback()
{
cout << "Normal callback" << endl;
}
// 类静态函数
class Test
{
public:
static void printFinCallback()
{
cout << "Static callback" << endl;
}
};
// 仿函数,重载()运算符
struct Functor {
void operator() ()
{
cout << "Functor callback" << endl;
}
};
print("test 1", printFinCallback);
print("test 2", Test::printFinCallback);
print("test 3", Functor());
// 直接传入lambda表达式
print("test 4", [] () {
cout << "Lambda callback" << endl;
});
std::bind绑定器
std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。
主要两大作用:
- 将可调用对象与其参数一起绑定成一个仿函数
- 将参数个数大于1的可调用对象转成1员或者n-1元可调用对象,即只绑定部分参数
举个栗子比喻一下,可调用对象就像一个枪,而参数就是子弹,一个函数的完整调用就是装子弹开枪的过程,而绑定器就相当于装子弹,装完子弹,放到一边,给枪命个名(std::function),比如biubiu枪,然后去做别的事情,当我要开biubiu枪(调用函数)的时候就不用装弹了,直接拿起来就开枪了。
代码示例:
class A
{
public:
int i = 0; // C++11支持非静态成员变量就地初始化
void output(int x, int y)
{
std::cout << x << " "<< y <<std::endl;
}
}
int main()
{
A a; // 声明一个对象
// f绑定了实例化的a,因为非静态成员函数需要传入this指针,所以需要传入a的地址,然后两个参数进行占位,等到真正调用的时候再传入参数
std::function<void(int, int)> f = std::bind(&A::output, &a, std::placeholders::_1, std::placeholders::_2);
fr(1, 2); //传入参数1,2 整个过程就跟a.output(1,2);一样 输出:1 2
// fr绑定类A的成员变量i,然后把对象a的地址传入fr,就相当于fr == a.i;
std::function<int&(void)> fr = std::bind(&A::i, &a);
fr_i() = 123; // 等同于a.i = 123;
std::cout <<a.i<<std::endl;//输出:123
}
lambda表达式
lambda定义:就地定义一个匿名函数,并且可以捕获一定范围内的变量语法形式:
[ 捕获列表 ] ( 参数列表 ) 函数选项 -> 返回值类型 { 表达式 } ;
在C++11中,lambda表达式的返回值是通过前面介绍的返回值后置语法来定义的,所以大多数情况lambda表达式的返回值是非常明显的,所以C++11允许省略lambda表达式的返回值定义,变成如下形式:
[ 捕获列表 ] ( 参数列表 ) { 表达式 } ;
没有参数列表的时候还能省略空参数列表,变成如下形式:
[ 捕获列表 ] { 表达式 } ;
代码示例:
auto f = [](int a)->int{return a + 1;};
f(1);
auto f1 = [](int a){return a + 1; };
f1(2);
auto f3 = []{return 3;};
f3();
- 捕获范围变量以及意义
写法 | 含义 |
---|---|
[ ] | 不捕获任何变量 |
[&] | 引用捕获外部作用域所有变量,在表达式内部可以改变外部变量 |
[=] | 按值捕获外部作用域所有变量,无法在表达式内部改变外部变量 |
[this] | 捕获当前对象的this指针,表达式内部就可以调用对象的成员变量成员函数 |
- 没有捕获任何变量的lambda表达式可以转换为普通函数指针
using func_t = int(*)(int);
func_t f = [](int a){return a ;};
f(123);
lambda表达式相关用法
1.简化库函数调用
std::vector<int> v = {1,2,3,4,5};
int even_count = 0;
for_each(v.begin(),v.end(),[&even_count](int val)
{
if(!(val & 1))
{
++even_count;
}
});
std::cout << "nums of even " << even_count <<std::endl;
2.std::function
typedef std::function<void ()> FinFunction;
void f(FinFunction callback)
{
if (callback)
callback();
}
int main()
{
f([]()
{
std::cout << "lambda" <<std::endl;
});
}
3.std::thread实现小闭包线程
#include <thread>
#include <iostream>
int main()
{
std::thread thread1([](){
std::cout<<"lambda thread called." <<std::endl;
});
thread1.join(); // join会阻塞main函数,detach不是阻塞的,但是thread1出了main的作用域就被析构了
return 0;
}