目录
- [3. 可变参数模板](#3. 可变参数模板)
- [3.1 基本语法](#3.1 基本语法)
- [3.2 包扩展](#3.2 包扩展)
- [3.3 完美转发](#3.3 完美转发)
- [3.4 emplace系列接口](#3.4 emplace系列接口)
- [4 lambda](#4 lambda)
- [4.1 lambda表达式语法](#4.1 lambda表达式语法)
- [4.2 捕捉列表](#4.2 捕捉列表)
- [4.3 lambda的应用](#4.3 lambda的应用)
- [5. 包装器](#5. 包装器)
- [5.1 基本概念](#5.1 基本概念)
- [5.2 function的应用](#5.2 function的应用)
- [5.3 bind](#5.3 bind)
- [5.3.1 基本语法](#5.3.1 基本语法)
- [5.3.2 占位符](#5.3.2 占位符)
- [5.3.3 bind的使用](#5.3.3 bind的使用)
- 总结
3. 可变参数模板
3.1 基本语法
C++11支持可变参数模板,也就是说支持可变数量参数的函数模板和类模板,可变数目的参数被称
为参数包,存在两种参数包:模板参数包,表示零或多个模板参数;函数参数包:表示零或多个函
数参数。

template <class ...Args> void Func(Args... args) {}
template <class ...Args> void Func(Args&... args) {}
template <class ...Args> void Func(Args&&... args) {}
可变参数模板的原理跟模板类似,本质还是去实例化对应类型和个数的多个函数。
可变参数模板在编译期展开,所有操作都发生在编译阶段。
这一点至关重要。参数包不是数组,不能像数组那样在运行时通过下标 args[i] 去访问。所有的"遍历"和"处理"都必须借助编译期的技术手段来完成:
示例:
template <class ...Args>
void Print(Args... args)
{
// 不支持
// 可变参数模板编译时解析
// 下面是运行获取和解析,所以不支持这样用
/* for (size_t i = 0; i < sizeof...(args); i++)
{
cout << args[i] << " ";
}*/
cout << sizeof...(args) << endl;
cout << endl;
}
int main()
{
double x = 2.2;
//Print(); // 包里有0个参数
Print(1); // 包里有1个参数
Print(1, string("xxxxx")); // 包里有2个参数
Print(1.1, string("xxxxx"), x); // 包里有3个参数
return 0;
}
3.2 包扩展
对于一个参数包,我们除了能计算它的参数个数,我们能做的唯一的事情就是扩展它 。当扩展一个包时,我们还要提供用于每个扩展元素的模式 。扩展一个包就是将它分解为构成的元素,对每个元素应用模式,获得扩展后的列表。我们通过在模式的右边放一个省略号(...)来触发扩展操作。底层的实现细节如图1所示。
C++还支持更复杂的包扩展,直接将参数包依次展开依次作为实参给一个函数去处理。
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)
{
// N个参数,第一个传给x,剩下N-1参数传给ShowList的第二个参数包
ShowList(args...);
}
//void ShowList(double x)
//{
// cout << x << " ";
// ShowList();
//}
//
//void ShowList(string x, double x3)
//{
// cout << x << " ";
// ShowList(x3);
//}
//
//void ShowList(int x, string x2, double x3)
//{
// cout << x << " ";
// ShowList(x2, x3);
//}
//
//void Print(int x1, string x2, double x3)
//{
// ShowList(x1, x2, x3);
//}
int main()
{
double x = 2.2;
Print(1, string("xxxxx"), x); // 包里有3个参数
return 0;
}
调用链展开过程:
┌─────────────────────────────────────────────────────────────────┐
│ 第1步:main 调用 Print(1, "xxxxx", 2.2) │
│ → Print 的参数包 args = {1, "xxxxx", 2.2} │
│ → Print 调用 ShowList(args...) │
│ → ShowList(1, "xxxxx", 2.2) 被实例化 │
│ │
│ 第2步:ShowList(1, "xxxxx", 2.2) │
│ → T = int, x = 1 │
│ → Args = {string, double}, args = {"xxxxx", 2.2} │
│ → 打印: 1 │
│ → 调用 ShowList(args...) → ShowList("xxxxx", 2.2) │
│ │
│ 第3步:ShowList("xxxxx", 2.2) │
│ → T = string, x = "xxxxx" │
│ → Args = {double}, args = {2.2} │
│ → 打印: xxxxx │
│ → 调用 ShowList(args...) → ShowList(2.2) │
│ │
│ 第4步:ShowList(2.2) │
│ → T = double, x = 2.2 │
│ → Args = {}, args = 空包 │
│ → 打印: 2.2 │
│ → 调用 ShowList(args...) → ShowList() │
│ │
│ 第5步:ShowList() │
│ → 打印换行 │
│ → 递归结束 │
└─────────────────────────────────────────────────────────────────┘
3.3 完美转发
完美转发是 C++11 引入的重要特性,它允许一个函数模板将它的参数原封不动地 转发给另一个函数,保持参数的所有性质不变(包括类型、左值/右值属性、const/volatile 修饰等)。
假设我们有一个工厂函数 make_obj,它接收参数并转发给 Object 的构造函数:
template <typename T>
void func(T param) {
// 想要把 param 转发给另一个函数
other_func(param); // ❌ 参数的性质可能丢失!
}
问题 :param 在 func 内部是一个左值 (即使传入的是右值),转发给 other_func 时会丢失右值属性,导致无法调用移动构造函数等,造成性能损失。
要实现完美转发,需要三个要素:
| 要素 | 作用 |
|---|---|
| 万能引用(Universal Reference) | T&& 能接收左值和右值 |
| 引用折叠(Reference Collapsing) | 处理 T&& 与具体类型的组合 |
std::forward |
保持参数的原始值类别进行转发 |
std::forward 的作用:
std::forward<T>(arg) 根据模板参数 T 的类型来决定返回左值还是右值:
T 的类型 |
forward<T>(arg) 返回 |
|---|---|
T(非引用) |
右值 |
T&(左值引用) |
左值 |
T&&(右值引用) |
右值 |
示例:
#include <iostream>
#include <utility>
using namespace std;
// ============ 目标函数 ============
void target(int& x) {
cout << "target(int&): 左值引用, x = " << x << endl;
}
void target(int&& x) {
cout << "target(int&&): 右值引用, x = " << x << endl;
}
void target(const int& x) {
cout << "target(const int&): const 左值引用, x = " << x << endl;
}
// ============ 不完美转发的包装函数 ============
template <typename T>
void bad_wrapper(T arg) { // 值传递,丢失引用属性
target(arg); // 永远传递左值
}
// ============ 完美转发的包装函数 ============
template <typename T>
void good_wrapper(T&& arg) { // 万能引用
target(std::forward<T>(arg)); // 完美转发
}
int main() {
int a = 10;
const int b = 20;
cout << "===== 直接调用目标函数 =====" << endl;
target(a); // int& → target(int&)
target(30); // 右值 → target(int&&)
target(b); // const int& → target(const int&)
cout << "\n===== 不完美转发(值传递) =====" << endl;
bad_wrapper(a); // 传递给 target 的是左值 → target(int&)
bad_wrapper(30); // 传递给 target 的是左值 → target(int&) ❌ 丢失右值属性
bad_wrapper(b); // 传递给 target 的是左值 → target(int&) ❌ 丢失 const
cout << "\n===== 完美转发 =====" << endl;
good_wrapper(a); // 保持左值 → target(int&)
good_wrapper(30); // 保持右值 → target(int&&) ✅
good_wrapper(b); // 保持 const 左值 → target(const int&) ✅
return 0;
}
输出:
===== 直接调用目标函数 =====
target(int&): 左值引用, x = 10
target(int&&): 右值引用, x = 30
target(const int&): const 左值引用, x = 20
===== 不完美转发(值传递) =====
target(int&): 左值引用, x = 10
target(int&): 左值引用, x = 30 ← 右值变左值,丢失了移动语义
target(int&): 左值引用, x = 20 ← 丢失了 const
===== 完美转发 =====
target(int&): 左值引用, x = 10
target(int&&): 右值引用, x = 30 ← 右值被正确保持
target(const int&): const 左值引用, x = 20 ← const 被正确保持
3.4 empalce系列接口
emplace 系列接口是 C++11 引入的特性,主要存在于标准库容器中,用于就地构造元素,避免不必要的拷贝或移动操作,从而提升性能。
template <class... Args> void emplace_back (Args&&... args);
template <class... Args> iterator emplace (const_iterator position,
Args&&... args);
3.4.1 vector::emplace_back 简化实现:
#include <iostream>
#include <utility>
using namespace std;
class MyVector {
private:
char* data; // 原始内存
size_t size; // 当前元素个数
size_t capacity; // 容量
public:
MyVector() : data(nullptr), size(0), capacity(0) {}
template <class... Args>
void emplace_back(Args&&... args) {
// 1. 检查是否需要扩容
if (size >= capacity) {
capacity = capacity == 0 ? 1 : capacity * 2;
char* new_data = new char[capacity * sizeof(int)];
// 将已有对象移动到新内存(简化实现)
if (data) {
for (size_t i = 0; i &lt; size; ++i) {
int* p = (int*)(new_data + i * sizeof(int));
int* old_p = (int*)(data + i * sizeof(int));
new (p) int(std::move(*old_p));
}
delete[] data;
}
data = new_data;
}
// 2. 在尾部位置就地构造对象
int* pos = (int*)(data + size * sizeof(int));
new (pos) int(std::forward&lt;Args&gt;(args)...);
++size;
}
void print() {
for (size_t i = 0; i < size; ++i) {
cout << (int)(data + i * sizeof(int)) << " ";
}
cout << endl;
}
~MyVector() {
// 析构对象(简化版)
delete[] data;
}
};
int main() {
MyVector v;
v.emplace_back(10); // 直接构造 int
v.emplace_back(20);
v.emplace_back(30);
v.print(); // 输出: 10 20 30
return 0;
}
3.4.2 使用场景对比:
vector<Student> v;
v.reserve(3);
Student s1("张三", 20);
// 方式1:push_back - 传入已有对象
v.push_back(s1); // 拷贝构造一次
v.push_back(std::move(s1)); // 移动构造一次
// 方式2:push_back - 传入临时对象
v.push_back(Student("李四", 21)); // 构造 + 移动
// 方式3:emplace_back - 就地构造
v.emplace_back("王五", 22); // 只有一次构造,无拷贝/移动
// 方式4:emplace_back - 移动语义
Student s2("赵六", 23);
v.emplace_back(std::move(s2)); // 只有一次移动构造
4 lambda
4.1 lambda表达式语法
lambda 表达式本质是一个匿名函数对象,和普通函数不一样的是,它可以定义在函数内部。 从 lambda 表达式的语法使用层面来讲它没有类型,所以我们通常使用 auto 或者模板参数定义的对象来接收 lambda 对象。
基本语法结构:
[capture](parameters) -> return_type { body }
| 部分 | 说明 | 是否必须 |
|---|---|---|
[capture] |
捕获列表:指定从外部作用域捕获哪些变量 | ✅ 必须 |
(parameters) |
参数列表:函数的形参 | ❌ 可省略(无参时) |
-> return_type |
返回类型:指定返回值类型 | ❌ 可省略(可推导时) |
{ body } |
函数体:具体的执行代码 | ✅ 必须 |
示例:
#include <iostream>
using namespace std;
int main() {
// 最简单的 lambda:无参数,无捕获,无返回值
[] { cout << "Hello Lambda!" << endl; };
// ↑ ↑ ↑
// 捕获 参数 函数体
// 要执行 lambda,需要调用它
[] { cout << "Hello Lambda!" << endl; }(); // 加 () 立即执行
// 或者赋值给变量后调用
auto sayHello = [] { cout << "Hello Lambda!" << endl; };
sayHello(); // 调用
return 0;
}
4.2 捕捉列表
C++ lambda 捕获规则复述:
- lambda 表达式默认仅可以使用 lambda 函数体与参数内的变量;若要使用外层作用域的变量,就需要进行捕获。
- 显式捕获 在捕获列表里写明变量,分为值捕获 、引用捕获 ;多个捕获变量之间用逗号隔开。 示例:
[x, y, &z]:x、y 为值捕获,z 为引用捕获。 - 隐式捕获 不在捕获列表逐个列出变量,交给编译器自动捕获用到的外层变量:
[=]:隐式值捕获[&]:隐式引用捕获
- 混合捕获(隐式 + 显式一起用)
[=, &x]:其余变量全部隐式值捕获,唯独 x 使用引用捕获[&, x, y]:其余变量全部隐式引用捕获,唯独 x、y 使用值捕获
混合捕获强制规则:捕获列表第一个元素必须是
&或=;
- 开头为
&(默认引用捕获),后面显式列出的变量只能是值捕获;- 开头为
=(默认值捕获),后面显式列出的变量只能是引用捕获。
示例:
int a = 1, b = 2, c = 3;
// ========== 值捕获 ==========
[a] { cout << a << endl; }; // 只捕获 a(值拷贝)
[a, b] { cout << a + b << endl; }; // 捕获 a 和 b(值拷贝)
// ========== 引用捕获 ==========
[&a] { a = 10; }; // 捕获 a(引用),可以修改外部变量
[&a, &b] { a = b; }; // 捕获 a 和 b(引用)
// ========== 隐式捕获 ==========
[=] { cout << a + b + c << endl; }; // 全部值捕获(拷贝)
[&] { a = b + c; }; // 全部引用捕获
// ========== 混合捕获 ==========
[=, &a] { a = b + c; }; // a 引用捕获,其他全部值捕获
[&, a] { cout << a + b << endl; }; // a 值捕获,其他全部引用捕获
4.3 lambda的应用
在接触 lambda 表达式之前,C++ 里可供我们使用的可调用对象只有函数指针 与仿函数。 函数指针的类型声明书写繁琐;而仿函数则需要专门定义一个类,编写起来同样比较麻烦。 而借助 lambda 表达式来快速创建可调用对象,写法更加简洁高效,使用起来十分方便。
struct Goods
{
string _name; // 名字
double _price; // 价格
int _evaluate; // 评价
// ...
Goods(const char* str, double price, int evaluate)
:_name(str)
, _price(price)
, _evaluate(evaluate)
{}
};
struct ComparePriceLess
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price < gr._price;
}
};
struct ComparePriceGreater
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price > gr._price;
}
};
//
int main()
{
vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3}, { "菠萝", 1.5, 4 } };
// 类似这样的场景,我们实现仿函数对象或者函数指针支持商品中
// 不同项的比较,相对还是比较麻烦的,那么这里lambda就很好用了
//sort(v.begin(), v.end(), ComparePriceLess());
//sort(v.begin(), v.end(), ComparePriceGreater());
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._price < g2._price;
});
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._price > g2._price;
});
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._evaluate < g2._evaluate;
});
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._evaluate > g2._evaluate;
});
return 0;
}
5. 包装器
5.1 基本概念
std::function 是一个类模板 ,定义在 <functional> 头文件中。它的实例可以包装存储任何可调用对象,包括:
-
普通函数
-
函数指针
-
仿函数(函数对象)
-
Lambda 表达式
-
std::bind表达式 -
类的成员函数(静态/非静态)
template
class function; // 未定义
template <class Ret, class... Args>
class function<Ret(Args...)>; // 特化版本
核心特征:
| 特性 | 说明 |
|---|---|
| 类型统一 | 不同可调用对象可以被统一包装为 std::function 类型 |
| 类型安全 | 编译期检查参数类型和返回值类型 |
| 目标存储 | 存储的可调用对象称为 function 的"目标" |
| 空状态 | 不含目标时称为"空" |
| 异常安全 | 调用空的 std::function 会抛出 std::bad_function_call 异常 |
#include <iostream>
#include <functional>
using namespace std;
// ============ 普通函数 ============
int f(int a, int b) {
return a + b;
}
// ============ 仿函数 ============
struct Functor {
public:
int operator()(int a, int b) {
return a + b;
}
};
// ============ 包含成员函数的类 ============
class Plus {
public:
Plus(int n = 10) : _n(n) {}
// 静态成员函数
static int plusi(int a, int b) {
return a + b;
}
// 普通成员函数(有隐含的 this 指针)
double plusd(double a, double b) {
return (a + b) * _n;
}
private:
int _n;
};
int main() {
// ========== 包装各种可调用对象 ==========
function<int(int, int)> f1 = f; // 普通函数
function<int(int, int)> f2 = Functor(); // 仿函数
function<int(int, int)> f3 = [](int a, int b) { // Lambda
return a + b;
};
cout << f1(1, 1) << endl; // 输出: 2
cout << f2(1, 1) << endl; // 输出: 2
cout << f3(1, 1) << endl; // 输出: 2
// ========== 包装静态成员函数 ==========
// 成员函数要指定类域并且前面加 & 才能获取地址
function<int(int, int)> f4 = &Plus::plusi;
cout << f4(1, 1) << endl; // 输出: 2
// ========== 包装普通成员函数 ==========
// 普通成员函数还有一个隐含的 this 指针参数
// 所以绑定时传对象或者对象的指针都可以
// 方式1:传入对象指针 (Plus*)
function<double(Plus*, double, double)> f5 = &Plus::plusd;
Plus pd;
cout << f5(&pd, 1.1, 1.1) << endl; // 输出: (2.2) * 10 = 22
// 方式2:传入对象本身 (Plus)
function<double(Plus, double, double)> f6 = &Plus::plusd;
cout << f6(pd, 1.1, 1.1) << endl; // 输出: 22
// 方式3:传入右值对象 (Plus&&)
function<double(Plus&&, double, double)> f7 = &Plus::plusd;
cout << f7(move(pd), 1.1, 1.1) << endl; // 输出: 22
cout << f7(Plus(), 1.1, 1.1) << endl; // 输出: (2.2) * 10 = 22
return 0;
}
5.2 function的应用
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
-
有效的算符为
'+'、'-'、'*'和'/'。 -
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
-
两个整数之间的除法总是 向零截断 。
-
表达式中不含除零运算。
-
输入是一个根据逆波兰表示法表示的算术表达式。
-
答案及所有中间计算结果可以用 32 位 整数表示。
// 传统⽅式的实现
class Solution {
public:
int evalRPN(vector& tokens) {
stackst;
for (auto& str : tokens)
{
if (str == "+" || str == "-" || str == "" || str == "/")
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
switch (str[0])
{
case '+':
st.push(left + right);
break;
case '-':
st.push(left - right);
break;
case '':
st.push(left * right);
break;
case '/':
st.push(left / right);
break;
}
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
// 使⽤map映射string和function的⽅式实现
// 这种⽅式的最⼤优势之⼀是⽅便扩展,假设还有其他运算,我们增加map中的映射即可
class Solution {
public:
int evalRPN(vector& tokens) {
stackst;
// function作为map的映射可调⽤对象的类型
map<string, function<int(int, int)>> opFuncMap = {
{"+", [](int x, int y) {return x + y; }},
{"-", [](int x, int y) {return x - y; }},
{"*", [](int x, int y) {return x * y; }},
{"/", [](int x, int y) {return x / y; }}
};
for (auto& str : tokens)
{
if (opFuncMap.count(str)) // 操作符
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
int ret = opFuncMap[str](left, right);
st.push(ret);
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
5.3 bind
std::bind 是 C++11 引入的函数适配器 ,它可以将一个可调用对象(函数、仿函数、Lambda、成员函数等)的某些参数预先绑定 为固定值,或调整参数顺序,从而生成一个新的可调用对象。
// 版本1:自动推导返回类型
template <class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);
// 版本2:显式指定返回类型
template <class Ret, class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);
5.3.1 基本语法
auto newCallable = bind(callable, arg_list);
-
callable:被包装的可调用对象(函数、仿函数、Lambda、成员函数等) -
arg_list:逗号分隔的参数列表,对应callable的参数 -
newCallable:返回的新的可调用对象
5.3.2 占位符
arg_list 中的参数可能包含形如 _n 的名字(n 是整数),这些参数是占位符,表示新可调用对象的参数位置:
| 占位符 | 含义 |
|---|---|
_1 |
newCallable 的第一个参数 |
_2 |
newCallable 的第二个参数 |
_n |
newCallable 的第 n 个参数 |
5.3.3 bind的使用
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
// 使用占位符
using namespace std::placeholders;
// ==================== 普通函数 ====================
int calculate(int a, int b, int c) {
return (a + b) * c;
}
// ==================== 测试用的类 ====================
class Calculator {
private:
int factor;
public:
Calculator(int f = 10) : factor(f) {}
// 静态成员函数
static int add(int a, int b) {
return a + b;
}
// 普通成员函数(有隐含的 this 指针)
int multiply(int a, int b) const {
return (a + b) * factor;
}
};
// ==================== main 函数 ====================
int main() {
cout << "========== std::bind 核心特性 ==========\n" << endl;
// ===== 特性1:固定部分参数(最常用) =====
cout << "【特性1】固定部分参数" << endl;
cout << "----------------------------------------" << endl;
// calculate 需要3个参数:(a+b)*c
// 固定 c = 10,变成只需要2个参数
auto calc_with_c10 = bind(calculate, _1, _2, 10);
cout << "calc_with_c10(5, 3) = " << calc_with_c10(5, 3) << endl; // (5+3)*10 = 80
// 固定 a = 100,b = 20,变成只需要1个参数
auto calc_fixed_ab = bind(calculate, 100, 20, _1);
cout << "calc_fixed_ab(5) = " << calc_fixed_ab(5) << endl; // (100+20)*5 = 600
// 固定所有参数,变成无参函数
auto calc_all_fixed = bind(calculate, 5, 3, 2);
cout << "calc_all_fixed() = " << calc_all_fixed() << endl; // (5+3)*2 = 16
cout << "\n";
// ===== 特性2:调整参数顺序 =====
cout << "【特性2】调整参数顺序" << endl;
cout << "----------------------------------------" << endl;
// 正常顺序:a = _1, b = _2
auto sub_normal = bind(minus<int>(), _1, _2);
cout << "sub_normal(10, 5) = " << sub_normal(10, 5) << endl; // 10-5 = 5
// 交换顺序:a = _2, b = _1
auto sub_swapped = bind(minus<int>(), _2, _1);
cout << "sub_swapped(10, 5) = " << sub_swapped(10, 5) << endl; // 5-10 = -5
cout << "\n";
// ===== 特性3:绑定成员函数 =====
cout << "【特性3】绑定成员函数" << endl;
cout << "----------------------------------------" << endl;
Calculator calc(5);
// 3.1 静态成员函数(没有 this,和普通函数一样)
auto static_bind = bind(Calculator::add, _1, _2);
cout << "static_bind(100, 200) = " << static_bind(100, 200) << endl; // 300
// 3.2 普通成员函数(需要绑定对象)
// 方式1:绑定对象指针
auto member_bind1 = bind(&Calculator::multiply, &calc, _1, _2);
cout << "member_bind1(3, 4) = " << member_bind1(3, 4) << endl; // (3+4)*5 = 35
// 方式2:绑定对象本身 + 固定参数
auto member_bind2 = bind(&Calculator::multiply, calc, 10, _1);
cout << "member_bind2(5) = " << member_bind2(5) << endl; // (10+5)*5 = 75
cout << "\n";
// ===== 特性4:STL 算法中的应用 =====
cout << "【特性4】STL 算法中的应用" << endl;
cout << "----------------------------------------" << endl;
vector<int> numbers = {10, 25, 30, 45, 50, 65, 70};
// 统计大于 40 的元素个数
int threshold = 40;
auto greater_than = bind(greater<int>(), _1, threshold);
int count = count_if(numbers.begin(), numbers.end(), greater_than);
cout << "大于 " << threshold << " 的元素个数: " << count << endl; // 3 (45,65,70)
// 每个元素乘以 2 再加 5
vector<int> result;
result.resize(numbers.size());
auto transform_func = bind(plus<int>(), bind(multiplies<int>(), _1, 2), 5);
transform(numbers.begin(), numbers.end(), result.begin(), transform_func);
cout << "原始: ";
for (int x : numbers) cout << x << " ";
cout << "\n变换后(每个*2+5): ";
for (int x : result) cout << x << " ";
cout << endl;
return 0;
}
运行结果:
========== std::bind 核心特性 ==========
【特性1】固定部分参数
calc_with_c10(5, 3) = 80
calc_fixed_ab(5) = 600
calc_all_fixed() = 16
【特性2】调整参数顺序
sub_normal(10, 5) = 5
sub_swapped(10, 5) = -5
【特性3】绑定成员函数
static_bind(100, 200) = 300
member_bind1(3, 4) = 35
member_bind2(5) = 75
【特性4】STL 算法中的应用
大于 40 的元素个数: 3
原始: 10 25 30 45 50 65 70
变换后(每个*2+5): 25 55 65 95 105 135 145
总结
可变参数模板是 C++11 引入的编译期特性,通过参数包接收任意数量和类型的参数,并在编译期展开为多个具体实例。其核心价值在于:一是配合包扩展和递归调用实现类型安全的变长参数处理;二是结合万能引用与 std::forward 实现完美转发,保持参数的左值/右值属性与 const 修饰不变;三是支撑 emplace 系列接口在容器中就地构造元素,避免不必要的拷贝和移动,从而提升性能。
lambda 表达式本质是定义在函数内部的匿名函数对象,通过捕获列表(值捕获、引用捕获、隐式捕获、混合捕获)灵活访问外层作用域变量。相比函数指针和仿函数,lambda 写法更简洁,特别适合作为排序比较器、STL 算法回调、std::function 包装对象等场景,是 C++ 现代编程中创建可调用对象的首选方式。
两者在实际开发中经常配合使用:可变参数模板负责编译期类型安全的参数传递与转发,lambda 负责提供简洁灵活的可调用对象,共同构成 C++11 泛型编程与函数式编程风格的重要基石。