目录
1 可变参数模板
2 emplace 系列接口
3 C++11 类新语法:default、delete
4 lambda 表达式
捕获规则
5 std::function 包装器
6 std::bind 适配器
小结
1 可变参数模板
C++11 支持可变参数模板,使用...代表参数包。分为模板参数包、函数参数包。
template<class ...Args> void Print(Args... args) { cout << sizeof...(args) << endl; // sizeof... 获取参数包中参数个数 }Args...模板参数包;args...函数参数包。sizeof...(args):编译期拿到参数包里面参数总个数。
参数包不能用 for 循环遍历,只能通过编译期递归展开。
递归展开示例:
#include<iostream> #include<string> using namespace std; //递归终止,参数包为空 void ShowList() { cout << endl; } template<class T, class ...Args> void ShowList(T x, Args... args) { cout << x << " "; ShowList(args...); // 剥掉第一个参数,剩下继续递归 } template<class ...Args> void Print(Args... args) { ShowList(args...); } int main() { Print(); Print(1); Print(1, string("hello")); Print(1.1, 2, "abc"); return 0; }原理:编译阶段实例化出多份重载函数,不是运行时解析参数。
2 emplace 系列接口
容器新增emplace_back / emplace,接收可变参数包。
push_back:需要已经构造完成对象,再拷贝 / 移动进入容器;emplace_back(Args&&... args):直接在容器结点内存上构造对象,不需要生成临时对象。
底层:可变参数包 + 完美转发
std::forward<Args>(args)...,把参数转发给结点的构造函数。
示例 list 模拟结点:
template<class T> struct ListNode { T _data; ListNode<T>* _prev; ListNode<T>* _next; //支持可变参数直接构造data template<class... Args> ListNode(Args&&... args) :_data(std::forward<Args>(args)...) {} };调用示例:
list<pair<string,int>> lt; lt.emplace_back("苹果",1); //直接传给pair构造,不用手动构造pair临时对象3 C++11 类新语法:default、delete
C++98 类 6 个默认成员:
- 构造函数
- 拷贝构造
- 拷贝赋值重载
- 析构函数
- 取地址
operator& - const 取地址
operator&
C++11 新增两个默认成员函数:移动构造、移动赋值。
规则:
- 没有自己写析构、拷贝构造、拷贝赋值,编译器才会自动生成默认移动构造 / 移动赋值。
=default:强制让编译器生成默认版本函数。=delete:删除函数,禁止调用该函数(C++98 时代把函数放 private 实现同样效果)。
示例:
class Person { public: Person(const char* name = "", int age = 0){} Person(const Person& p){} Person(Person&& p) = default; //强制生成默认移动构造 Person& operator=(Person&& p) = default; Person& operator=(const Person& p) = delete; //删除拷贝赋值,禁止使用 };另外 C++11 支持类内成员变量直接初始化缺省值,给初始化列表提供默认值。
4 lambda 表达式
lambda 本质:编译器自动生成匿名仿函数类。 完整语法:
[捕获列表](参数列表) -> 返回类型 {函数体};[]捕获列表,不可省略;()参数列表,无参数可以省略;->返回类型追踪返回类型,可以省略,编译器自动推导;{}函数体,不可省略。
捕获规则
表格
| 写法 | 含义 |
|---|---|
[x] | 值捕获,拷贝一份,默认 const,不能修改 |
[&x] | 引用捕获,直接使用外部变量本体,可以修改 |
[=] | 隐式值捕获:用到的变量全部值捕获 |
[&] | 隐式引用捕获:用到的变量全部引用捕获 |
[=, &a] | 其它值捕获,a 引用捕获 |
[&, a] | 其它引用捕获,a 值捕获 |
- mutable:加在参数括号后面,取消值捕获变量的 const 属性,可以修改拷贝副本,不会影响外部实参。
- 全局变量、静态局部变量不需要捕获,可以直接使用;lambda 写在全局域,捕获列表必须为空。
#include<iostream> #include<vector> #include<algorithm> #include<string> using namespace std; struct Goods { string _name; double _price; int _evaluate; Goods(const char* str, double price, int evaluate) :_name(str),_price(price),_evaluate(evaluate) {} }; int main() { vector<Goods> v = { {"苹果", 2.1, 5}, {"香蕉",3,4},{"橙子",2.2,3},{"菠萝",1.5,4} }; sort(v.begin(),v.end(),[](const Goods& g1,const Goods& g2){ return g1._price < g2._price; }); return 0; }lambda 底层原理:编译器生成一个仿函数类,捕获的变量变成类的成员;调用 lambda 等价调用仿函数
operator()。
5 std::function 包装器
头文件<functional>。std::function<Ret(Args...)>:统一包装各类可调用对象:普通函数、函数指针、仿函数、lambda 表达式、bind 返回对象。 解决不同可调用对象类型不一样,无法统一保存的问题。 如果function对象没有包装目标,调用会抛出bad_function_call异常。
#include<functional> #include<iostream> using namespace std; int f(int a,int b) { return a+b; } struct Functor { int operator()(int a,int b) { return a+b; } }; int main() { function<int(int,int)> f1 = f; function<int(int,int)> f2 = Functor(); function<int(int,int)> f3 = [](int a,int b){return a+b;}; cout << f1(1,2) << endl; cout << f2(1,2) << endl; cout << f3(1,2) << endl; return 0; }6 std::bind 适配器
头文件<functional>;bind 是函数适配器,包装可调用对象,可以:
- 绑定固定参数;
- 调整参数顺序; 占位符
_1 _2 _3定义在std::placeholders命名空间。
#include<functional> #include<iostream> using namespace std; using placeholders::_1; using placeholders::_2; int Sub(int a,int b) { return (a-b)*10; } int main() { auto sub1 = bind(Sub,_1,_2); cout << sub1(10,5) << endl; auto sub2 = bind(Sub,_2,_1); //交换参数顺序 cout << sub2(10,5) << endl; auto sub3 = bind(Sub,100,_1); //第一个参数固定100 cout << sub3(5) << endl; return 0; }小结
- 可变参数模板依靠编译递归展开参数包,
sizeof...(args)拿到参数数量;emplace依靠可变参数 + 完美转发直接在容器内存构造对象。 =default强制生成编译器默认函数;=delete删除函数,禁止调用。C++11 新增移动构造、移动赋值两个默认成员。- lambda 是编译器生成匿名仿函数,掌握捕获列表、mutable;适合算法 sort 等场景写临时逻辑。
std::function统一包装不同类型可调用对象;std::bind做参数绑定、顺序调整适配器。
后续 C++11 其余特性:智能指针、unordered 哈希容器、范围 for、override/final、string_view 等属于其他章节。