Biểu thức lambda trong ngôn ngữ C++11

Chào các bạn đang theo dõi khóa học lập trình trực tuyến ngôn ngữ C++.

Từ chuẩn C++11 trở đi, một biểu thức lambda, gọi tắt là lambda, là một cách tiện lợi để định nghĩa hàm không tên ngay tại nơi nó được gọi hoặc được truyền vào như một đối số của hàm khác.

Lambda thường được sử dụng để gói gọn một vài dòng code được truyền vào những thuật toán, hoặc những hàm đồng bộ (asynchronous methods).

Những thành phần chính của một lambda

Mình sử dụng một ví dụ sử dụng hàm std::sort trong thư viện với 3 tham số, trong đó, tham số thứ 3 mình sẽ truyền vào là một lambda để thuật toán xác định cần sắp xếp theo chiều tăng dần hay giảm dần.

#include <algorithm>
#include <cmath>

void abssort(float* x, unsigned n) {
    std::sort(x, x + n,
        // Lambda expression begins
        [](float a, float b) {
            return (std::abs(a) < std::abs(b));
        }
        // end of lambda expression
    );
}

Dưới đây là một hình ảnh minh họa cho một lambda:

(1) Mệnh đề bắt giữ (capture clause)

(2) Danh sách tham số

(3) Tính bền vững của lambda

(4) Ngoại lệ có thể xảy ra trong lambda (được nói đến trong bài học sau).

(5) Kiểu trả về của lambda

(6) Phần thân lambda

Chúng ta sẽ phân tích chi tiết từng thành phần của một lambda.

Mệnh đề bắt giữ (capture clause)

Một biểu thức lambda có thể khai báo thêm biến mới bên trong nó (từ chuẩn C++14 trở đi), và nó còn có thể truy cập, hoặc tham chiếu đến những biến bên trong khối lệnh chứa nó.

Một lambda luôn bắt đầu với cặp ngoặc vuông [ ], và những biến cần được bắt giữ sẽ khai báo bên trong đó. Ký hiệu ( & ) là biến được truy cập bằng tham chiếu, bỏ ký hiệu ( & ) hoặc sử dụng cách khai báo [ = ] sẽ được hiểu là truy cập giá trị. Phần này có thể được bỏ trống, và được hiểu rằng lambda này không truy cập biến nào trong khối lệnh chứa nó.

Ví dụ:

Một lambda muốn truy cập biến total theo kiểu tham chiếu, và truy cập biến factor theo kiểu giá trị, những cách khai báo sau là tương đương:

[&total, factor]
[factor, &total]
[&, factor]
[factor, &]
[=, &total]
[&total, =]

Mệnh đề bắt giữ có thể thay đổi giá trị bên ngoài lambda thông qua hình thức truy cập kiểu tham chiếu.

Danh sách tham số

Ngoài khả năng bắt giữ các biến bên ngoài, lambda còn có thể nhận đối số bằng cách khai báo danh sách tham số.

auto y = [] (int first, int second)
{
    return first + second;
};

Một lambda còn có thể nhận một lambda khác làm đối số.

Tính bền vững trong một lambda (mutable)

Nếu chúng ta thêm từ khóa mutable vào một lambda, nó cho phép lambda thay đổi giá trị những biến được bắt giữ theo giá trị.

Kiểu trả về của một lambda

Chúng ta có thể trả về bất kỳ kiểu dữ liệu nào giống như hàm thông thường. Ví dụ:

auto x1 = [](int i){ return i; }; // OK: return type is int

Tuy nhiên, để chương trình được rõ ràng hơn, chúng ta nên viết lambda có kiểu trả về như sau:

auto x1 = [](int i) -> int
{
	return i;
};

Thêm khai báo -> int giúp việc đọc hiểu lambda dễ dàng hơn.

Phần thân của một lambda

Phần thân của một lambda có thể:

  • sử dụng những biến được bắt giữ trong mệnh đề bắt giữ.
  • sử dụng các tham số.
  • sử dụng các biến được khai báo bên trong struct/class chứa nó thông qua con trỏ this.
  • sử dụng các biến toàn cục, biến static.
#include <iostream>
using namespace std;

int main()
{
   int m = 0;
   int n = 0;
    
   auto func = [&, n] (int a) mutable 
   {
       m = ++n + a;
   };
    
   func(4);
   cout << m << endl << n << endl;
}

Ví dụ trên đưa ra một lambda, nó bắt giữ biến n theo kiểu giá trị, và ngầm định bắt giữ biến m theo kiểu tham chiếu.

Kết quả in ra là:

5
0

Vì biến n được bắt giữ theo kiểu tham chiếu, giá trị của nó được bảo tồn sau khi lambda được gọi. Ngược lại, biến m bị thay đổi giá trị khi tham chiếu được sử dụng.

========================================================

Chúng ta vừa đi qua những phần căn bản nhất của một biểu thức lambda (gọi tắt là lambda). Hi vọng các bạn có thể tự tìm hiểu thêm về những phần nâng cao hơn của lambda qua một số đường dẫn tham khảo dưới đây:

https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=vs-2017

https://www.cprogramming.com/c++11/c++11-lambda-closures.html

Hẹn gặp lại các bạn trong bài học tiếp theo trong khóa học lập trình C++ hướng thực hành.

Mọi ý kiến đóng góp hoặc thắc mắc có thể đặt câu hỏi trực tiếp tại diễn đàn.

www.daynhauhoc.com

7 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?