
目录四、可变参数模版1. 概念及简单定义2.参数包展开五、emplace系列接口六、函数包装器1.function2.bind四、可变参数模版1. 概念及简单定义所谓可变参数模板就是允许一个模板接收任意数量的模板参数。最基本的写法如下templateclass... Args void ShowList(Args... args) { }class... Args表示声明了一个模板参数包。这里的Args 并不是一个普通的模板参数而是一组模板参数。也就是说普通模板可能是templateclass T这里的T只表示一个类型。而templateclass... Args 中的Args可以表示0 个类型 1 个类型 2 个类型 3 个类型 …… N 个类型因此称为模板参数包。再来看Args... args这里的args称为函数参数包。Args 保存一组类型args 保存与这些类型对应的一组实际参数因此可变参数模板不仅意味着参数数量可以变化。还意味着每个参数的类型也可以不同。这也是可变参数模板非常强大的地方。可用sizeof...计算参数包中到底有多少个参数templateclass... Args void Func(Args... args) { cout sizeof...(Args) endl; cout sizeof...(args) endl; }2.参数包展开参数包代表的是“一组参数”不能直接把整个参数包当成一个普通对象使用。所以这样是不对的templateclass... Args void ShowList(Args... args) { cout args endl; }所以如果想分别处理参数包中的每一个参数就需要想办法将参数args一个个拿出来这个过程就叫参数包展开。#include iostream using namespace std; // 递归终止函数 void ShowList() { cout endl; } // 展开函数 templateclass T, class... Args void ShowList(T value, Args... args) { cout value ; ShowList(args...); } int main() { ShowList(); ShowList(1); ShowList(1, 2); ShowList(1, 2, 3); ShowList(1, 2, 3, 4); return 0; }重点看函数参数T value, Args... args可以把它理解成两部分T value接收当前参数列表中的第一个参数Args... args接收剩余的所有参数。也就是说每调用一次函数都会先从当前参数中取出第一个参数交给value处理再把后面的参数保存在args...中。处理完当前的value后再通过ShowList(args...);递归处理剩余参数。于是整个参数包就会被一个一个展开直到没有参数为止。因此递归展开参数包的核心思想就是每次取出第一个参数进行处理再把剩余参数继续交给下一次递归调用。可变参数模板的参数类型可以完全不同五、emplace系列接口C11新增的emplace系列接口就是一个非常典型的应用。在 C11 之前我们向容器尾部插入元素经常使用push_back()现在可以使用更高效的emplace_back()了listDate lt; lt.push_back(Date(2025, 7, 18)); lt.emplace_back(2025, 7, 18);class Date { public: Date(int year 1, int month 1, int day 1) :_year(year) , _month(month) , _day(day) { cout Date构造函数 endl; } Date(const Date d) :_year(d._year) , _month(d._month) , _day(d._day) { cout Date拷贝构造 endl; } private: int _year; int _month; int _day; }; int main() { listDate lt; lt.emplace_back(2026, 1, 2); cout endl; lt.push_back(Date(2026, 1, 2)); return 0; }当节点存储浅拷贝类型时emplace_back仅调用一次构造函数而push_back需要调用一次构造函数和一次拷贝构造。因此对于此类类型emplace_back的开销更小。六、函数包装器C11引入了函数包装器他可以封装函数方便调用不同的函数。1.functionfunction的本质是一个类模版实例出的对象可以存储其他调用对象头文件是functional#include iostream #include functional using namespace std; //函数 int f1(int a, int b) { return a b; } //仿函数 struct F2 { int operator()(int a, int b) { return a - b; } }f2; //lambda表达式 auto f3 [](int a, int b)-int{ return a * b; }; int main() { functionint(int, int) f; f f1; cout f(8, 10) endl; f f2; cout f(8, 10) endl; f f3; cout f(8, 10) endl; return 0; }定义function时注意要显式实例化要接收对象的参数和返回值调用时直接用函数调用的方式进行传参。注意用function包装非静态成员函数时别忘记第一个参数是this指针显式实例化时要将对象指针类型写在参数列表中class Student { public: Student(string name, int no) :_name(name) , _no(no) { } void Print() const { cout _name _no endl; } static int add(int x, int y) { cout static_add ; return x y; } private: string _name; int _no; }; int main() { functionvoid(Student*) Func1 Student::Print; //包装类内成员函数(内部走-*) functionvoid(Student) Func2 Student::Print; //包装类内成员函数(内部走.*) functionint(int, int) Func3 Student::add; //包装类内静态成员函数 int x 1, y 10; Student s1 { guoshi, 20260101 }; Func1(s1); Func2(s1); cout Func3(x, y) endl; return 0; }2.bindbind 是一个函数模板头文件functional它接收一个可调用对象和一组参数返回一个新的可调用对象——新对象可以调整原函数的传参顺序、给任意位置预置固定的值也可以把成员函数的对象绑死。#include iostream #include functional using namespace std; using namespace std::placeholders; // _1 _2 _3 ... 都在这个空间里 void Print(int a, int b, int c) { cout a b c endl; } int main() { auto f bind(Print, _1, _2, _3); // 包装 Print 函数 f(1, 2, 3); return 0; }运行结果1 2 3三个要点bind 的第一个参数是被包装的可调用对象后面的位置才是参数_1 _2 _3是占位符表示调用时传入的第 1、2、3 个实参——它跟函数的第几个参数没有关系bind 的返回类型是编译器生成的、手写不出来的类型所以要用auto接收占位符也可以按需一个个展开不必整个空间都放开using std::placeholders::_1; using std::placeholders::_2; using std::placeholders::_3;占位符的排放顺序就是实参交给原函数的顺序int main() { auto f bind(Print, _1, _3, _2); // 把第 2、3 个位置的代号换一下 f(10, 20, 30); return 0; } 运行结果10 30 20也可以给任意位置预置固定的值int main() { auto f bind(Print, _1, 100, _2); // 中间那个参数固定成 100 f(7, 8); return 0; } 7 100 8