Biểu thức lambda trong C ++ 11 là gì? Khi nào tôi sẽ sử dụng một? Loại vấn đề nào họ giải quyết mà không thể có trước khi giới thiệu?
Một vài ví dụ và trường hợp sử dụng sẽ hữu ích.
Biểu thức lambda trong C ++ 11 là gì? Khi nào tôi sẽ sử dụng một? Loại vấn đề nào họ giải quyết mà không thể có trước khi giới thiệu?
Một vài ví dụ và trường hợp sử dụng sẽ hữu ích.
Câu trả lời:
C ++ bao gồm các hàm chung hữu ích như std::for_each
và std::transform
, có thể rất tiện dụng. Thật không may, chúng cũng có thể khá cồng kềnh khi sử dụng, đặc biệt nếu functor mà bạn muốn áp dụng là duy nhất cho chức năng cụ thể.
#include <algorithm>
#include <vector>
namespace {
struct f {
void operator()(int) {
// do something
}
};
}
void func(std::vector<int>& v) {
f f;
std::for_each(v.begin(), v.end(), f);
}
Nếu bạn chỉ sử dụng f
một lần và ở nơi cụ thể đó, có vẻ như quá mức cần thiết để viết cả một lớp chỉ để làm một cái gì đó tầm thường và một cái.
Trong C ++ 03, bạn có thể muốn viết một cái gì đó như sau, để giữ functor cục bộ:
void func2(std::vector<int>& v) {
struct {
void operator()(int) {
// do something
}
} f;
std::for_each(v.begin(), v.end(), f);
}
tuy nhiên điều này không được phép, f
không thể truyền cho hàm mẫu trong C ++ 03.
C ++ 11 giới thiệu lambdas cho phép bạn viết một functor nội tuyến, ẩn danh để thay thế struct f
. Đối với các ví dụ đơn giản nhỏ, điều này có thể dễ đọc hơn (nó giữ mọi thứ ở một nơi) và có khả năng duy trì đơn giản hơn, ví dụ ở dạng đơn giản nhất:
void func3(std::vector<int>& v) {
std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}
Các hàm Lambda chỉ là đường cú pháp cho hàm functor ẩn danh.
Trong các trường hợp đơn giản, kiểu trả về của lambda được suy ra cho bạn, ví dụ:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) { return d < 0.00001 ? 0 : d; }
);
}
tuy nhiên khi bạn bắt đầu viết lambdas phức tạp hơn, bạn sẽ nhanh chóng gặp phải trường hợp loại trả về không thể được suy ra bởi trình biên dịch, ví dụ:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
Để giải quyết vấn đề này, bạn được phép chỉ định rõ ràng loại trả về cho hàm lambda, sử dụng -> T
:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) -> double {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
Cho đến nay chúng ta không sử dụng bất cứ thứ gì ngoài những gì được truyền cho lambda trong đó, nhưng chúng ta cũng có thể sử dụng các biến khác, trong lambda. Nếu bạn muốn truy cập các biến khác, bạn có thể sử dụng mệnh đề chụp ( []
biểu thức), cho đến nay vẫn chưa được sử dụng trong các ví dụ này, ví dụ:
void func5(std::vector<double>& v, const double& epsilon) {
std::transform(v.begin(), v.end(), v.begin(),
[epsilon](double d) -> double {
if (d < epsilon) {
return 0;
} else {
return d;
}
});
}
Bạn có thể chụp theo cả tham chiếu và giá trị, mà bạn có thể chỉ định bằng cách sử dụng &
và =
tương ứng:
[&epsilon]
chụp bằng cách tham khảo[&]
nắm bắt tất cả các biến được sử dụng trong lambda bằng cách tham chiếu[=]
nắm bắt tất cả các biến được sử dụng trong lambda theo giá trị[&, epsilon]
nắm bắt các biến như với [&], nhưng epsilon theo giá trị[=, &epsilon]
nắm bắt các biến như với [=], nhưng epsilon bằng cách tham chiếuĐược tạo ra operator()
là const
theo mặc định, với ngụ ý rằng ảnh chụp sẽ được const
khi bạn truy cập chúng bằng cách mặc định. Điều này có tác dụng là mỗi cuộc gọi có cùng một đầu vào sẽ tạo ra cùng một kết quả, tuy nhiên bạn có thể đánh dấu lambda làmutable
để yêu cầu cuộc gọi operator()
được tạo ra không const
.
const
luôn luôn là ...
()
- nó được thông qua dưới dạng lambda đối số 0, nhưng vì () const
không khớp với lambda, nên nó tìm kiếm một chuyển đổi loại cho phép nó, bao gồm cả diễn viên ngầm -to-function-con trỏ, và sau đó gọi đó! Lén lút!
std::function<double(int, bool)> f = [](int a, bool b) -> double { ... };
Nhưng thông thường, chúng tôi để trình biên dịch suy ra loại: auto f = [](int a, bool b) -> double { ... };
(và đừng quên #include <functional>
)
return d < 0.00001 ? 0 : d;
được đảm bảo trả về gấp đôi, khi một trong các toán hạng là hằng số nguyên (đó là do quy tắc quảng cáo ngầm của toán tử ?: Trong đó toán hạng 2 và 3 được cân bằng với nhau thông qua số học thông thường chuyển đổi bất kể cái nào được chọn). Thay đổi thành 0.0 : d
có lẽ sẽ làm cho ví dụ dễ hiểu hơn.
Khái niệm C ++ của hàm lambda bắt nguồn từ phép tính lambda và lập trình hàm. Lambda là một hàm không tên, rất hữu ích (trong lập trình thực tế, không phải lý thuyết) cho các đoạn mã ngắn không thể sử dụng lại và không đáng để đặt tên.
Trong C ++, hàm lambda được định nghĩa như thế này
[]() { } // barebone lambda
hoặc trong tất cả vinh quang của nó
[]() mutable -> T { } // T is the return type, still lacking throw()
[]
là danh sách chụp, ()
danh sách đối số và {}
thân hàm.
Danh sách chụp xác định những gì từ bên ngoài lambda nên có sẵn bên trong thân hàm và làm thế nào. Nó có thể là:
Bạn có thể trộn bất kỳ mục nào ở trên trong danh sách được phân tách bằng dấu phẩy [x, &y]
.
Danh sách đối số giống như trong bất kỳ hàm C ++ nào khác.
Mã sẽ được thực thi khi lambda thực sự được gọi.
Nếu lambda chỉ có một câu lệnh return, kiểu trả về có thể được bỏ qua và có kiểu ẩn decltype(return_statement)
.
Nếu lambda được đánh dấu là có thể thay đổi (ví dụ []() mutable { }
), nó được phép thay đổi các giá trị đã bị bắt bởi giá trị.
Thư viện được xác định theo tiêu chuẩn ISO được hưởng lợi rất nhiều từ lambdas và tăng khả năng sử dụng một số thanh vì giờ đây người dùng không phải làm lộn xộn mã của họ với các hàm xử lý nhỏ trong một phạm vi có thể truy cập được.
Trong C ++ 14 lambdas đã được mở rộng bởi các đề xuất khác nhau.
Một phần tử của danh sách chụp bây giờ có thể được khởi tạo với =
. Điều này cho phép đổi tên các biến và để nắm bắt bằng cách di chuyển. Một ví dụ được lấy từ tiêu chuẩn:
int x = 4;
auto y = [&r = x, x = x+1]()->int {
r += 2;
return x+2;
}(); // Updates ::x to 6, and initializes y to 7.
và một bức được lấy từ Wikipedia chỉ ra cách chụp với std::move
:
auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};
Bây giờ Lambdas có thể là chung ( auto
sẽ tương đương với T
ở đây nếu
T
là một đối số mẫu kiểu ở đâu đó trong phạm vi xung quanh):
auto lambda = [](auto x, auto y) {return x + y;};
C ++ 14 cho phép các kiểu trả về được suy ra cho mọi hàm và không giới hạn nó ở các hàm của biểu mẫu return expression;
. Điều này cũng được mở rộng đến lambdas.
r = &x; r += 2;
, nhưng điều này xảy ra với giá trị ban đầu là 4.
Các biểu thức Lambda thường được sử dụng để đóng gói các thuật toán để chúng có thể được chuyển sang một hàm khác. Tuy nhiên, có thể thực hiện lambda ngay lập tức theo định nghĩa :
[&](){ ...your code... }(); // immediately executed lambda expression
có chức năng tương đương với
{ ...your code... } // simple code block
Điều này làm cho biểu thức lambda trở thành một công cụ mạnh mẽ để tái cấu trúc các hàm phức tạp . Bạn bắt đầu bằng cách gói một phần mã trong hàm lambda như được hiển thị ở trên. Quá trình tham số hóa rõ ràng sau đó có thể được thực hiện dần dần với thử nghiệm trung gian sau mỗi bước. Khi bạn có khối mã được tham số hóa đầy đủ (như được thể hiện bằng cách loại bỏ &
), bạn có thể di chuyển mã đến một vị trí bên ngoài và biến nó thành một chức năng bình thường.
Tương tự, bạn có thể sử dụng biểu thức lambda để khởi tạo các biến dựa trên kết quả của thuật toán ...
int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!
Là một cách phân vùng logic chương trình của bạn , bạn thậm chí có thể thấy hữu ích khi truyền biểu thức lambda làm đối số cho biểu thức lambda khác ...
[&]( std::function<void()> algorithm ) // wrapper section
{
...your wrapper code...
algorithm();
...your wrapper code...
}
([&]() // algorithm section
{
...your algorithm code...
});
Biểu thức Lambda cũng cho phép bạn tạo các hàm lồng nhau có tên , đây có thể là một cách thuận tiện để tránh logic trùng lặp. Sử dụng lambdas có tên cũng có xu hướng dễ dàng hơn một chút (so với lambdas nội tuyến ẩn danh) khi chuyển một hàm không tầm thường như một tham số cho một chức năng khác. Lưu ý: đừng quên dấu chấm phẩy sau khi đóng ngoặc nhọn.
auto algorithm = [&]( double x, double m, double b ) -> double
{
return m*x+b;
};
int a=algorithm(1,2,3), b=algorithm(4,5,6);
Nếu hồ sơ tiếp theo cho thấy chi phí khởi tạo đáng kể cho đối tượng hàm, bạn có thể chọn viết lại phần này như một hàm bình thường.
if
tuyên bố : if ([i]{ for (char j : i) if (!isspace(j)) return false ; return true ; }()) // i is all whitespace
, giả sử i
là mộtstd::string
[](){}();
.
(lambda: None)()
Cú pháp của Python dễ đọc hơn nhiều.
main() {{{{((([](){{}}())));}}}}
Đáp án
Q: Biểu thức lambda trong C ++ 11 là gì?
A: Dưới mui xe, nó là đối tượng của một lớp được tạo tự động với toán tử nạp chồng () const . Đối tượng như vậy được gọi là đóng và được tạo bởi trình biên dịch. Khái niệm 'đóng cửa' này gần với khái niệm liên kết từ C ++ 11. Nhưng lambdas thường tạo mã tốt hơn. Và các cuộc gọi thông qua việc đóng cửa cho phép nội tuyến đầy đủ.
Q: Khi nào tôi sẽ sử dụng nó?
Trả lời: Để xác định "logic đơn giản và nhỏ" và yêu cầu trình biên dịch thực hiện tạo từ câu hỏi trước. Bạn cung cấp cho trình biên dịch một số biểu thức mà bạn muốn ở bên trong toán tử (). Tất cả các trình biên dịch công cụ khác sẽ tạo ra cho bạn.
Q: Loại vấn đề nào họ giải quyết mà không thể có trước khi giới thiệu?
Trả lời: Đó là một số loại cú pháp như toán tử nạp chồng thay vì các hàm để thêm tùy chỉnh , thao tác subrtact ... Nhưng nó lưu nhiều dòng mã không cần thiết để bọc 1-3 dòng logic thực cho một số lớp, v.v.! Một số kỹ sư nghĩ rằng nếu số lượng dòng nhỏ hơn thì sẽ ít xảy ra lỗi hơn (tôi cũng nghĩ vậy)
Ví dụ về cách sử dụng
auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);
Extras về lambdas, không được bao phủ bởi câu hỏi. Bỏ qua phần này nếu bạn không quan tâm
1. Giá trị thu được. Những gì bạn có thể chụp
1.1. Bạn có thể tham chiếu đến một biến có thời lượng lưu trữ tĩnh trong lambdas. Tất cả đều bị bắt.
1.2. Bạn có thể sử dụng lambda để chụp các giá trị "theo giá trị". Trong trường hợp như vậy, các vars bị bắt sẽ được sao chép vào đối tượng hàm (bao đóng).
[captureVar1,captureVar2](int arg1){}
1.3. Bạn có thể chụp được tham khảo. & - trong ngữ cảnh này có nghĩa là tham chiếu, không phải con trỏ.
[&captureVar1,&captureVar2](int arg1){}
1.4. Nó tồn tại ký hiệu để nắm bắt tất cả các bình không tĩnh theo giá trị hoặc theo tham chiếu
[=](int arg1){} // capture all not-static vars by value
[&](int arg1){} // capture all not-static vars by reference
1.5. Nó tồn tại ký hiệu để nắm bắt tất cả các bình không tĩnh theo giá trị, hoặc bằng cách tham chiếu và chỉ định smth. hơn. Ví dụ: Chụp tất cả các vars không tĩnh theo giá trị, nhưng bằng cách bắt tham chiếu Param2
[=,&Param2](int arg1){}
Chụp tất cả các lọ không tĩnh bằng tham chiếu, nhưng bằng cách chụp giá trị Param2
[&,Param2](int arg1){}
2. Khấu trừ kiểu trả về
2.1. Kiểu trả về Lambda có thể được suy ra nếu lambda là một biểu thức. Hoặc bạn có thể chỉ định rõ ràng nó.
[=](int arg1)->trailing_return_type{return trailing_return_type();}
Nếu lambda có nhiều hơn một biểu thức, thì kiểu trả về phải được chỉ định thông qua kiểu trả về theo sau. Ngoài ra, cú pháp tương tự có thể được áp dụng cho các chức năng tự động và chức năng thành viên
3. Giá trị thu được. Những gì bạn không thể nắm bắt
3.1. Bạn chỉ có thể chụp các vars cục bộ, không phải biến thành viên của đối tượng.
4. Biến đổi
4.1 !! Lambda không phải là một con trỏ hàm và nó không phải là một hàm ẩn danh, nhưng lambdas không có khả năng bắt giữ có thể được chuyển đổi hoàn toàn thành một con trỏ hàm.
ps
Thông tin thêm về thông tin ngữ pháp lambda có thể được tìm thấy trong Bản thảo làm việc cho Ngôn ngữ lập trình C ++ # 337, 2012-01-16, 5.1.2. Biểu thức Lambda, tr.88
Trong C ++ 14, tính năng bổ sung có tên là "init Capture" đã được thêm vào. Nó cho phép thực hiện tuyên bố một cách nghiêm túc các thành viên dữ liệu đóng cửa:
auto toFloat = [](int value) { return float(value);};
auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};
[&,=Param2](int arg1){}
dường như không phải là cú pháp hợp lệ. Mẫu chính xác sẽ là[&,Param2](int arg1){}
Hàm lambda là một hàm ẩn danh mà bạn tạo nội tuyến. Nó có thể nắm bắt các biến như một số đã giải thích, (ví dụ: http://www.stroustrup.com/C++11FAQ.html#lambda ) nhưng có một số hạn chế. Ví dụ: nếu có giao diện gọi lại như thế này,
void apply(void (*f)(int)) {
f(10);
f(20);
f(30);
}
bạn có thể viết một hàm tại chỗ để sử dụng nó giống như hàm được truyền vào bên dưới:
int col=0;
void output() {
apply([](int data) {
cout << data << ((++col % 10) ? ' ' : '\n');
});
}
Nhưng bạn không thể làm điều này:
void output(int n) {
int col=0;
apply([&col,n](int data) {
cout << data << ((++col % 10) ? ' ' : '\n');
});
}
vì những hạn chế trong tiêu chuẩn C ++ 11. Nếu bạn muốn sử dụng ảnh chụp, bạn phải dựa vào thư viện và
#include <functional>
(hoặc một số thư viện STL khác như thuật toán để lấy nó một cách gián tiếp) và sau đó làm việc với hàm std :: thay vì truyền các hàm bình thường như các tham số như thế này:
#include <functional>
void apply(std::function<void(int)> f) {
f(10);
f(20);
f(30);
}
void output(int width) {
int col;
apply([width,&col](int data) {
cout << data << ((++col % width) ? ' ' : '\n');
});
}
apply
là một mẫu chấp nhận functor, nó sẽ hoạt động
Một trong những lời giải thích tốt nhất lambda expression
được đưa ra từ tác giả của C ++ Bjarne Stroustrup trong cuốn sách ***The C++ Programming Language***
chương 11 ( ISBN-13: 978-0321563842 ):
What is a lambda expression?
Một biểu thức lambda , đôi khi cũng được gọi là hàm lambda hoặc (nói đúng không chính xác, nhưng thông tục) là lambda , là một ký hiệu đơn giản để xác định và sử dụng một đối tượng hàm ẩn danh . Thay vì định nghĩa một lớp được đặt tên bằng một toán tử (), sau đó tạo một đối tượng của lớp đó và cuối cùng gọi nó, chúng ta có thể sử dụng một tốc ký.
When would I use one?
Điều này đặc biệt hữu ích khi chúng ta muốn chuyển một hoạt động như một đối số cho một thuật toán. Trong ngữ cảnh của giao diện người dùng đồ họa (và các nơi khác), các hoạt động như vậy thường được gọi là các cuộc gọi lại .
What class of problem do they solve that wasn't possible prior to their introduction?
Ở đây tôi đoán mọi hành động được thực hiện với biểu thức lambda có thể được giải quyết mà không có chúng, nhưng với nhiều mã hơn và độ phức tạp lớn hơn nhiều. Lambda biểu hiện đây là cách tối ưu hóa cho mã của bạn và là cách làm cho nó hấp dẫn hơn. Như buồn bởi Stroustup:
cách tối ưu hóa hiệu quả
Some examples
thông qua biểu thức lambda
void print_modulo(const vector<int>& v, ostream& os, int m) // output v[i] to os if v[i]%m==0
{
for_each(begin(v),end(v),
[&os,m](int x) {
if (x%m==0) os << x << '\n';
});
}
hoặc thông qua chức năng
class Modulo_print {
ostream& os; // members to hold the capture list int m;
public:
Modulo_print(ostream& s, int mm) :os(s), m(mm) {}
void operator()(int x) const
{
if (x%m==0) os << x << '\n';
}
};
hoặc thậm chí
void print_modulo(const vector<int>& v, ostream& os, int m)
// output v[i] to os if v[i]%m==0
{
class Modulo_print {
ostream& os; // members to hold the capture list
int m;
public:
Modulo_print (ostream& s, int mm) :os(s), m(mm) {}
void operator()(int x) const
{
if (x%m==0) os << x << '\n';
}
};
for_each(begin(v),end(v),Modulo_print{os,m});
}
Nếu bạn cần bạn có thể đặt tên lambda expression
như dưới đây:
void print_modulo(const vector<int>& v, ostream& os, int m)
// output v[i] to os if v[i]%m==0
{
auto Modulo_print = [&os,m] (int x) { if (x%m==0) os << x << '\n'; };
for_each(begin(v),end(v),Modulo_print);
}
Hoặc giả sử một mẫu đơn giản khác
void TestFunctions::simpleLambda() {
bool sensitive = true;
std::vector<int> v = std::vector<int>({1,33,3,4,5,6,7});
sort(v.begin(),v.end(),
[sensitive](int x, int y) {
printf("\n%i\n", x < y);
return sensitive ? x < y : abs(x) < abs(y);
});
printf("sorted");
for_each(v.begin(), v.end(),
[](int x) {
printf("x - %i;", x);
}
);
}
sẽ tạo tiếp theo
0
1
0
1
0
1
0
1
0
1
0 sắp xếpx - 1; x - 3; x - 4; x - 5; x - 6; x - 7; x - 33;
[]
- đây là danh sách chụp hoặc lambda introducer
: nếu lambdas
không có quyền truy cập vào môi trường địa phương của họ, chúng tôi có thể sử dụng nó.
Trích dẫn từ cuốn sách:
Ký tự đầu tiên của biểu thức lambda luôn là [ . Một người giới thiệu lambda có thể có nhiều hình thức:
• [] : danh sách chụp trống. Điều này ngụ ý rằng không có tên địa phương từ bối cảnh xung quanh có thể được sử dụng trong cơ thể lambda. Đối với các biểu thức lambda như vậy, dữ liệu được lấy từ các đối số hoặc từ các biến không nhắm mục tiêu.
• [&] : chụp ngầm bằng tham chiếu. Tất cả các tên địa phương có thể được sử dụng. Tất cả các biến cục bộ được truy cập bằng cách tham khảo.
• [=] : hoàn toàn nắm bắt theo giá trị. Tất cả các tên địa phương có thể được sử dụng. Tất cả các tên đều đề cập đến các bản sao của các biến cục bộ được lấy tại điểm gọi của biểu thức lambda.
• [danh sách chụp]: chụp rõ ràng; danh sách chụp là danh sách tên của các biến cục bộ sẽ được ghi lại (nghĩa là được lưu trữ trong đối tượng) theo tham chiếu hoặc theo giá trị. Các biến có tên đứng trước & được bắt bằng tham chiếu. Các biến khác được nắm bắt bởi giá trị. Một danh sách chụp cũng có thể chứa cái này và tên được theo sau bởi ... như các phần tử.
• [&, Capture-list] : hoàn toàn nắm bắt bằng cách tham chiếu tất cả các biến cục bộ với các tên không được nhắc đến trong danh sách. Danh sách chụp có thể chứa điều này. Tên được liệt kê không thể đứng trước &. Các biến có tên trong danh sách chụp được ghi theo giá trị.
• [=, Capture-list] : hoàn toàn nắm bắt theo giá trị tất cả các biến cục bộ với các tên không được đề cập trong danh sách. Danh sách chụp không thể chứa cái này. Các tên được liệt kê phải được đi trước bởi &. Các biến thể có tên trong danh sách chụp được tham chiếu.
Lưu ý rằng một tên cục bộ có trước & luôn được bắt bởi tham chiếu và một tên cục bộ không được đặt trước bởi & luôn được ghi theo giá trị. Chỉ chụp bằng tham chiếu cho phép sửa đổi các biến trong môi trường gọi.
Additional
Lambda expression
định dạng
Tham khảo thêm:
for (int x : v) { if (x % m == 0) os << x << '\n';}
Vâng, một cách sử dụng thực tế mà tôi đã tìm ra là giảm mã tấm nồi hơi. Ví dụ:
void process_z_vec(vector<int>& vec)
{
auto print_2d = [](const vector<int>& board, int bsize)
{
for(int i = 0; i<bsize; i++)
{
for(int j=0; j<bsize; j++)
{
cout << board[bsize*i+j] << " ";
}
cout << "\n";
}
};
// Do sth with the vec.
print_2d(vec,x_size);
// Do sth else with the vec.
print_2d(vec,y_size);
//...
}
Không có lambda, bạn có thể cần phải làm một cái gì đó cho các bsize
trường hợp khác nhau . Tất nhiên bạn có thể tạo một chức năng nhưng nếu bạn muốn giới hạn việc sử dụng trong phạm vi của chức năng người dùng linh hồn thì sao? bản chất của lambda đáp ứng yêu cầu này và tôi sử dụng nó cho trường hợp đó.
Các lambda trong c ++ được coi là "chức năng có sẵn". có nghĩa đen của nó trên đường đi, bạn xác định nó; sử dụng nó; và khi phạm vi hàm cha kết thúc, hàm lambda sẽ biến mất.
c ++ đã giới thiệu nó trong c ++ 11 và mọi người bắt đầu sử dụng nó như ở mọi nơi có thể. ví dụ và lambda là gì có thể tìm thấy ở đây https://en.cppreference.com/w/cpp/lingu/lambda
tôi sẽ mô tả cái không có nhưng cần thiết cho mọi lập trình viên c ++
Lambda không có nghĩa là sử dụng ở mọi nơi và mọi chức năng không thể được thay thế bằng lambda. Nó cũng không phải là nhanh nhất so với chức năng bình thường. bởi vì nó có một số chi phí cần được xử lý bởi lambda.
Nó chắc chắn sẽ giúp giảm số lượng dòng trong một số trường hợp. về cơ bản nó có thể được sử dụng cho phần mã, được gọi trong cùng một chức năng một hoặc nhiều lần và đoạn mã đó không cần thiết ở bất kỳ nơi nào khác để bạn có thể tạo chức năng độc lập cho nó.
Dưới đây là ví dụ cơ bản về lambda và những gì xảy ra trong nền.
Mã người dùng:
int main()
{
// Lambda & auto
int member=10;
auto endGame = [=](int a, int b){ return a+b+member;};
endGame(4,5);
return 0;
}
Cách biên dịch mở rộng nó:
int main()
{
int member = 10;
class __lambda_6_18
{
int member;
public:
inline /*constexpr */ int operator()(int a, int b) const
{
return a + b + member;
}
public: __lambda_6_18(int _member)
: member{_member}
{}
};
__lambda_6_18 endGame = __lambda_6_18{member};
endGame.operator()(4, 5);
return 0;
}
như bạn có thể thấy, loại chi phí nào được thêm vào khi bạn sử dụng nó. Vì vậy, nó không phải là ý tưởng tốt để sử dụng chúng ở khắp mọi nơi. nó có thể được sử dụng ở những nơi áp dụng chúng.
Một vấn đề nó giải quyết: Mã đơn giản hơn lambda cho một cuộc gọi trong hàm tạo sử dụng hàm tham số đầu ra để khởi tạo một thành viên const
Bạn có thể khởi tạo một thành viên const của lớp, với một lệnh gọi đến hàm đặt giá trị của nó bằng cách trả lại đầu ra của nó dưới dạng tham số đầu ra.