Lambda expression là một cách đơn giản và nhanh chóng để tạo một Functor. lambda expression là một prvalue mà kết quả của nó là một đối tượng gọi là closure object (closure object là object của class vô danh, duy nhất – closure type class), nó hoạt động như 1 function object. C++ bắt đầu hỗ trợ lambda expression từ C++11.
C++ lambda expression có thể truy cập đến các biến ở ngữ cảnh xung quanh (capture variables from the surrounding context) và thường được dùng như 1 tham số truyền vào cho 1 function khác. Với những hàm chỉ dùng ở một chỗ duy nhất thì thay vì phải tạo function hoặc functor với tên tuổi đàng hoàng thì chúng ta có thể đơn giản là dùng lambda để định nghĩa nó inline , nhanh chóng, thuận tiện.
Một lambda thông thường sẽ gồm 3 thành phần:- Capture list: []
- Parameter list (optional – có thể có hoặc không): ()
- Function body: {}
1 |
[](){} // Đây là một lambda rỗng, nó không làm gì cả và cũng không return giá trị gì. |
Capture list
[] là capture list. Theo mặc định, lambda không thể truy cập các biến trong enclosing scope của nó (enclosing scope của lambda tạm hiểu là scope có chứa khai báo lambda).
Capturing một biến sẽ giúp cho nó có thể được truy cập bên trong lambda (có thể là bản copy – “captured” by value hoặc reference – “captured” by reference). Biến được capture sẽ trở thành 1 phần của lambda và không cần phải truyền vào khi call lambda.
Ví dụ →
1 2 |
int a = 0; // Define an integer variable auto f = []() { return a + 10; }; // Conmpile error: 'a' cannot be accessed by lambda |
1 |
auto f = [a]() { return a + 10; }; // OK, 'a' is "captured" by value |
1 2 3 |
auto f = [&a]() { return a++; }; // OK, 'a' is "captured" by reference // Trong trường hơp "captured" by reference thì programmer/developer có trách nhiệm phải đảm bảo rằng biến a không bị hủy trước khi lambda được call. // Nếu a bị hủy trước khi lambda được call thì khi lambda được call có thể gây crash chương trình. |
1 2 |
// lambda is called. auto b = f(); // Call the lambda function. a is taken from the capture list and not passed here. |
Parameter list
() là parameter list, nó cũng giống với tham số của hàm bình thường thôi, nếu lambda không có tham số thì có thể dấu (), trừ khi bạn muốn khai báo Mutable Lambda (sẽ nói ở phần sau).
2 cách khai bao lambda bên dưới là như nhau:
1 2 |
auto f1 = [x](){ x.foo(); }; auto f2 = [x]{ x.foo(); }; |
Parameter list có thể sử dụng kiểu template (C++11 trở lên) hoặc auto (C++14 trở lên) thay vì kiểu dữ liệu chính xác. Ví dụ dưới đây là 2 lambda dùng để sort một vector cho tất cả các kiểu dữ liệu miễn là kiểu dữ liệu đó có toán tử so sánh <.
1 2 |
auto sort_cpp11 = [](std::vector<T>::const_reference lhs, std::vector<T>::const_reference rhs) { return lhs < rhs; }; auto sort_cpp14 = [](const auto &lhs, const auto &rhs) { return lhs < rhs; }; |
Function body
{} là thân hàm, cũng giống như thân hàm bình thường nó sẽ chứa các xử lý logic của hàm. Không có gì đặc biệt.
Calling a lambda
Lambda có thể được call sử dụng operator () giống như các functor bình thường →
1 2 3 4 5 |
int multiplier = 5; auto timesFive = [multiplier](int a) { return a * multiplier; }; std::out << timesFive(2); // Prints 10 multiplier = 15; std::out << timesFive(2); // Still prints 2*5 == 10 |
Return Type (Kiểu trả về) của lambda
Theo mặc định thì kiểu trả vể của lambda được nội suy từ thân hàm. Ví dụ lambda sau có return type là bool →
1 |
[](){ return true; }; |
1 |
[]() -> bool { return true; }; |
Mutable Lambda
Theo mặc định, trong lambda không được phép thay đổi giá trị của những biến được “captured by value”, những biến đó là immutable (không thể thay đổi).
1 2 |
int a = 0; // Define an integer variable auto f = [a]() { return a++; }; // Build lỗi vì thay đổi value của a ("captured by value") |
1 |
auto f = [a]() mutable { return a++; }; // Build OK |
1 |
auto f = [a]() mutable -> int { return a++; }; |
Ví dụ về sự thuận tiện của lambda
Giả sử ta cần sắp xếp 1 dãy số nguyên- Trước C++11: sử dụng Functor
1234567891011121314151617181920class islessthan{public:islessthan(int threshold) : _threshold(threshold) {}bool operator()(int value) const{return value < _threshold;}private:int _threshold;};// Declare a vectorconst int arr[] = { 1, 2, 3, 4, 5 };std::vector<int> vec(arr, arr+5);// Find a number that's less than a given input (assume this would have been function input)int threshold = 10;std::vector<int>::iterator it = std::find_if(vec.begin(), vec.end(), islessthan(threshold));
- Từ C++11: có thể sử dụng lambda rất đơn giản và gọn lẹ
123456// Declare a vectorstd::vector<int> vec{ 1, 2, 3, 4, 5 };// Find a number that's less than a given input (assume this would have been function input)int threshold = 10;auto it = std::find_if(vec.begin(), vec.end(), [threshold](int value) { return value < threshold; });
— Phạm Minh Tuấn (Shun) —