Lambda表达式是C++11引入的一种匿名函数,允许在代码中直接定义一个函数对象,而无需显式定义一个命名函数或函数对象类。
[捕获列表](参数列表) mutable 异常规范 -> 返回类型 { 函数体 }
#include <iostream>
int main() {
// 定义一个简单的Lambda表达式
auto lambda = []() { std::cout << "Hello, Lambda!" << std::endl; };
// 调用Lambda表达式
lambda();
return 0;
}
[]
:捕获列表,表示Lambda表达式可以访问的外部变量。()
:参数列表,类似于普通函数的参数列表。{}
:函数体,定义Lambda表达式的行为。
捕获列表用于指定Lambda表达式可以访问的外部变量。
[]
:不捕获任何变量。[=]
:默认值捕获,捕获所有外部变量,但捕获的变量是只读的。[&]
:默认引用捕获,捕获所有外部变量,捕获的变量是可修改的。[x]
:值捕获变量x
。[&x]
:引用捕获变量x
。[x, &y]
:值捕获x
,引用捕获y
。
#include <iostream>
int main() {
int x = 42;
int y = 100;
// 值捕获x,引用捕获y
auto lambda = [x, &y]() {
std::cout << "x: " << x << ", y: " << y << std::endl;
y = 200; // 修改y的值
};
lambda(); // 输出x: 42, y: 100
std::cout << "After lambda: y = " << y << std::endl; // 输出200
return 0;
}
[=]
:默认值捕获,所有捕获的变量是只读的。[&]
:默认引用捕获,所有捕获的变量是可修改的。
捕获的变量生命周期必须长于Lambda表达式的生命周期,否则会导致未定义行为。
参数列表用于定义Lambda表达式的参数。
#include <iostream>
int main() {
// 定义带参数的Lambda表达式
auto lambda = [](int a, int b) { return a + b; };
std::cout << "Sum: " << lambda(3, 4) << std::endl; // 输出7
return 0;
}
Lambda表达式支持可变参数,使用折叠表达式可以处理多个参数。
#include <iostream>
int main() {
// 定义可变参数的Lambda表达式
auto lambda = [](auto... args) {
return (args + ...); // 折叠表达式,计算所有参数的和
};
std::cout << "Sum: " << lambda(1, 2, 3, 4) << std::endl; // 输出10
return 0;
}
mutable
关键字允许修改值捕获的变量。
#include <iostream>
int main() {
int x = 42;
// 值捕获x,可变Lambda
auto lambda = [x]() mutable {
x = 100; // 允许修改捕获的值
std::cout << "x: " << x << std::endl;
};
lambda(); // 输出x: 100
std::cout << "Original x: " << x << std::endl; // 输出42,原始x未改变
return 0;
}
Lambda表达式可以使用noexcept
指定不抛出异常。
#include <iostream>
int main() {
// 定义不抛出异常的Lambda表达式
auto lambda = []() noexcept { std::cout << "No exception" << std::endl; };
lambda();
return 0;
}
Lambda表达式可以显式指定返回类型,也可以自动推导返回类型。
#include <iostream>
int main() {
// 显式指定返回类型
auto lambda = [](int a, int b) -> int { return a + b; };
std::cout << "Sum: " << lambda(3, 4) << std::endl; // 输出7
return 0;
}
Lambda表达式的类型是编译器生成的匿名类型,无法直接使用。
- 使用
decltype
获取Lambda表达式的类型。 - 使用
std::function
进行类型擦除。
#include <iostream>
#include <functional>
int main() {
auto lambda = [](int x) { return x * 2; };
// 使用decltype获取Lambda的类型
decltype(lambda) lambda2 = lambda;
// 使用std::function进行类型擦除
std::function<int(int)> func = lambda;
std::cout << "Lambda result: " << lambda(42) << std::endl; // 输出84
std::cout << "Lambda2 result: " << lambda2(42) << std::endl; // 输出84
std::cout << "Function result: " << func(42) << std::endl; // 输出84
return 0;
}
std::function
可以用于存储任意可调用对象,包括Lambda表达式。
#include <iostream>
#include <functional>
int main() {
std::function<int(int)> func = [](int x) { return x * 2; };
std::cout << "Function result: " << func(42) << std::endl; // 输出84
return 0;
}
值捕获的变量是只读的,使用mutable
可以修改捕获的值。
#include <iostream>
int main() {
int x = 42;
// 值捕获x,不可变Lambda
auto lambda = [x]() {
// x = 100; // 错误:x是只读的
std::cout << "x: " << x << std::endl;
};
lambda(); // 输出x: 42
return 0;
}
引用捕获的变量是可修改的,需要注意变量的生命周期。
#include <iostream>
int main() {
int x = 42;
// 引用捕获变量x
auto lambda = [&x]() {
x = 100; // 修改x的值
std::cout << "x: " << x << std::endl;
};
lambda(); // 输出x: 100
std::cout << "Original x: " << x << std::endl; // 输出100
return 0;
}
默认捕获可以捕获所有外部变量,使用[=]
或[&]
。
#include <iostream>
int main() {
int x = 42;
int y = 100;
// 默认值捕获
auto lambda1 = [=]() { std::cout << "x: " << x << ", y: " << y << std::endl; };
// 默认引用捕获
auto lambda2 = [&]() { std::cout << "x: " << x << ", y: " << y << std::endl; };
lambda1(); // 输出x: 42, y: 100
lambda2(); // 输出x: 42, y: 100
return 0;
}
可以同时使用值捕获和引用捕获。
#include <iostream>
int main() {
int x = 42;
int y = 100;
// 值捕获x,引用捕获y
auto lambda = [x, &y]() {
std::cout << "x: " << x << ", y: " << y << std::endl;
y = 200; // 修改y的值
};
lambda(); // 输出x: 42, y: 100
std::cout << "After lambda: y = " << y << std::endl; // 输出200
return 0;
}
捕获this
指针可以访问当前对象的成员变量和成员函数。
#include <iostream>
class MyClass {
public:
void print() {
auto lambda = [this]() { std::cout << "x: " << x << std::endl; };
lambda();
}
private:
int x = 42;
};
int main() {
MyClass obj;
obj.print(); // 输出x: 42
return 0;
}
捕获动态分配的变量需要注意内存管理。
#include <iostream>
#include <memory>
int main() {
auto ptr = std::make_unique<int>(42);
// 捕获动态变量
auto lambda = [ptr = std::move(ptr)]() {
std::cout << "Value: " << *ptr << std::endl;
};
lambda(); // 输出Value: 42
return 0;
}
Lambda表达式可以作为函数参数传递。
#include <iostream>
void execute(std::function<void()> func) {
func();
}
int main() {
int x = 42;
// 将Lambda表达式作为函数参数传递
execute([x]() { std::cout << "x: " << x << std::endl; });
return 0;
}
Lambda表达式可以作为函数的返回值。
#include <iostream>
std::function<int(int)> createLambda() {
return [](int x) { return x * 2; };
}
int main() {
auto lambda = createLambda();
std::cout << "Result: " << lambda(42) << std::endl; // 输出84
return 0;
}
Lambda表达式常用于STL算法中。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用Lambda表达式遍历vector
std::for_each(vec.begin(), vec.end(), [](int x) { std::cout << x << " "; });
return 0;
}
Lambda表达式可以用于多线程编程。
#include <iostream>
#include <thread>
int main() {
int x = 42;
// 创建一个线程,使用Lambda表达式
std::thread t([&x]() {
std::cout << "Thread: x = " << x << std::endl;
});
t.join(); // 等待线程结束
return 0;
}
Lambda表达式可以用于事件处理。
#include <iostream>
#include <functional>
class EventHandler {
public:
void onEvent(std::function<void()> callback) {
callback();
}
};
int main() {
EventHandler handler;
// 使用Lambda表达式作为事件回调
handler.onEvent([]() { std::cout << "Event handled" << std::endl; });
return 0;
}
Lambda表达式可以用于回调函数。
#include <iostream>
void doWork(std::function<void()> callback) {
std::cout << "Working..." << std::endl;
callback();
}
int main() {
int x = 42;
// 使用Lambda表达式作为回调函数
doWork([x]() { std::cout << "Callback: x = " << x << std::endl; });
return 0;
}
Lambda表达式的开销主要来自于捕获变量的存储和调用。
Lambda表达式通常比std::function
性能更好,因为std::function
涉及类型擦除。
#include <iostream>
#include <chrono>
#include <functional>
void normalFunction(int n) {
for (int i = 0; i < n; ++i) {
// 空循环
}
}
int main() {
const int n = 100000000;
// 普通函数
auto start = std::chrono::high_resolution_clock::now();
normalFunction(n);
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Normal function: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
// Lambda表达式
start = std::chrono::high_resolution_clock::now();
auto lambda = [](int n) {
for (int i = 0; i < n; ++i) {
// 空循环
}
};
lambda(n);
end = std::chrono::high_resolution_clock::now();
std::cout << "Lambda: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
// std::function
start = std::chrono::high_resolution_clock::now();
std::function<void(int)> func = [](int n) {
for (int i = 0; i < n; ++i) {
// 空循环
}
};
func(n);
end = std::chrono::high_resolution_clock::now();
std::cout << "std::function: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
return 0;
}
捕获变量会增加Lambda表达式的开销,尤其是捕获大量变量时。
递归Lambda可以使用std::function
实现。
#include <iostream>
#include <functional>
int main() {
// 使用std::function实现递归Lambda
std::function<int(int)> factorial = [&](int n) {
return n == 0 ? 1 : n * factorial(n - 1);
};
std::cout << "Factorial of 5: " << factorial(5) << std::endl; // 输出120
return 0;
}
闭包是指Lambda表达式捕获的外部变量和函数体的组合。
#include <iostream>
int main() {
int x = 42;
// 闭包:捕获x并返回x的平方
auto closure = [x]() { return x * x; };
std::cout << "Closure result: " << closure() << std::endl; // 输出1764
return 0;
}
Lambda表达式可以捕获静态变量和全局变量。
#include <iostream>
int globalVar = 100;
int main() {
static int staticVar = 200;
// 捕获静态变量和全局变量
auto lambda = [=]() {
std::cout << "Global var: " << globalVar << ", Static var: " << staticVar << std::endl;
};
lambda(); // 输出Global var: 100, Static var: 200
return 0;
}
Lambda表达式可以捕获类的成员变量。
#include <iostream>
class MyClass {
public:
void print() {
auto lambda = [this]() { std::cout << "x: " << x << std::endl; };
lambda();
}
private:
int x = 42;
};
int main() {
MyClass obj;
obj.print(); // 输出x: 42
return 0;
}
捕获*this
可以捕获当前对象的副本。
#include <iostream>
class MyClass {
public:
void print() {
auto lambda = [*this]() { std::cout << "x: " << x << std::endl; };
lambda();
}
private:
int x = 42;
};
int main() {
MyClass obj;
obj.print(); // 输出x: 42
return 0;
}
Lambda表达式不能捕获constexpr
变量。
捕获的变量生命周期必须长于Lambda表达式的生命周期。
Lambda表达式不能捕获超出作用域的变量。
std::function
可以存储Lambda表达式。
#include <iostream>
#include <functional>
int main() {
std::function<int(int)> func = [](int x) { return x * 2; };
std::cout << "Function result: " << func(42) << std::endl; // 输出84
return 0;
}
std::bind
可以绑定参数,但不如Lambda表达式灵活。
#include <iostream>
#include <functional>
void print(int a, int b) {
std::cout << "a: " << a << ", b: " << b << std::endl;
}
int main() {
// 使用std::bind绑定参数
auto bindFunc = std::bind(print, 1, std::placeholders::_1);
bindFunc(2); // 输出a: 1, b: 2
// 使用Lambda表达式
auto lambda = [](int a, int b) { print(a, b); };
lambda(1, 2); // 输出a: 1, b: 2
return 0;
}
Lambda表达式可以用于多线程编程。
#include <iostream>
#include <thread>
int main() {
int x = 42;
// 创建一个线程,使用Lambda表达式
std::thread t([&x]() {
std::cout << "Thread: x = " << x << std::endl;
});
t.join(); // 等待线程结束
return 0;
}
Lambda表达式可以用于异步编程。
#include <iostream>
#include <future>
int main() {
// 使用std::async和Lambda表达式
auto future = std::async([]() {
std::cout << "Async task" << std::endl;
return 42;
});
std::cout << "Result: " << future.get() << std::endl; // 输出42
return 0;
}
Lambda表达式可以捕获std::unique_ptr
。
#include <iostream>
#include <memory>
int main() {
auto ptr = std::make_unique<int>(42);
// 捕获std::unique_ptr
auto lambda = [ptr = std::move(ptr)]() {
std::cout << "Value: " << *ptr << std::endl;
};
lambda(); // 输出Value: 42
return 0;
}
模板Lambda允许在Lambda表达式中使用模板参数。
#include <iostream>
int main() {
// 模板Lambda
auto lambda = []<typename T>(T x) { std::cout << "Type: " << typeid(T).name() << ", Value: " << x << std::endl; };
lambda(42); // 输出int
lambda(3.14); // 输出double
return 0;
}
C++23引入了更多Lambda表达式的新特性,如捕获constexpr
变量。
捕获的变量生命周期必须长于Lambda表达式的生命周期。
捕获的变量必须在Lambda表达式的作用域内。
捕获的变量在多线程环境中需要考虑线程安全。
捕获大量变量会增加Lambda表达式的开销。
- 在需要定义简单、临时的函数对象时。
- 在STL算法中使用。
- 在多线程编程中使用。
- 当函数逻辑复杂且需要复用时,建议使用命名函数。
- 当捕获的变量过多时,可能会导致代码可读性下降。
- 尽量减少捕获的变量数量。
- 避免使用
std::function
,除非必须进行类型擦除。
- 确保捕获的变量生命周期长于Lambda表达式的生命周期。
- 在多线程环境中注意线程安全。
#include <iostream>
int main() {
auto lambda = []() { std::cout << "Hello, Lambda!" << std::endl; };
lambda();
return 0;
}
#include <iostream>
int main() {
int x = 42;
auto lambda = [x]() { std::cout << "x: " << x << std::endl; };
lambda();
return 0;
}
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::for_each(vec.begin(), vec.end(), [](int x) { std::cout << x << " "; });
return 0;
}
#include <iostream>
#include <thread>
int main() {
int x = 42;
std::thread t([&x]() { std::cout << "Thread: x = " << x << std::endl; });
t.join();
return 0;
}
#include <iostream>
#include <functional>
int main() {
std::function<int(int)> factorial = [&](int n) {
return n == 0 ? 1 : n * factorial(n - 1);
};
std::cout << "Factorial of 5: " << factorial(5) << std::endl;
return 0;
}
编译器会将Lambda表达式转换为一个匿名的闭包类,并在其中生成一个operator()
重载。
闭包类包含捕获的变量,并重载operator()
。
捕获的变量存储在闭包类的成员变量中。
闭包类重载operator()
,用于调用Lambda表达式。
C++11引入了Lambda表达式。
C++14引入了泛型Lambda。
C++17引入了捕获*this
。
C++20引入了模板Lambda。
C++23引入了更多Lambda表达式的新特性。