C++ 11에서 람다 식(종종 "람다"라고 함)은 호출되거나 함수에 대한 인수로 전달되는 지점에서 익명 함수 개체를 정의하는 편리한 방법입니다. 람다는 일반적으로 알고리즘이나 비동기 메서드에 전달되는 소수의 코드 줄을 캡슐화하는 데 사용됩니다. 이 기사에서는 람다가 무엇인지 정의하고, 람다를 다른 프로그래밍 기술과 비교하고, 장점을 설명하고, 기본 예제를 제공합니다.
람다 표현식의 일부
ISO C++ 표준은 std::sort() 함수에 세 번째 인수로 전달되는 간단한 람다를 보여줍니다.
#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 ); }
이 다이어그램은 람다의 구성 요소를 보여줍니다.
Capture 절(C++ 사양에서는 람다 부트스트래핑이라고도 함)
매개변수 목록(선택 사항) ). (람다 선언자라고도 함)
변수 사양(선택 사항).
예외 사양(선택 사항).
후행 반환 유형(선택 사항).
"lambda 본문"
Capture 절
Lambda는 본문(C++14)에 새 변수를 도입할 수 있으며 액세스(또는 "캡처")할 수도 있습니다. 주변 범위의 변수. 람다는 캡처할 변수와 값 또는 참조로 캡처할지 여부를 지정하는 Capture 절(표준 구문의 람다 부트스트랩)로 시작합니다. 앰퍼샌드(&) 접두사가 붙은 변수는 참조로 액세스되고, 이 접두사가 없는 변수는 값으로 액세스됩니다.
빈 캡처 절 [ ]은 람다 식의 본문이 바깥쪽 범위의 변수에 액세스하지 않음을 나타냅니다.
기본 캡처 모드(표준 구문에서는 캡처-기본값)를 사용하여 람다에서 참조되는 외부 변수를 캡처하는 방법을 나타낼 수 있습니다. [&]는 참조로 참조되는 모든 변수를 캡처하는 것을 의미하고, [=]는 참조로 캡처하는 것을 의미합니다. 값 . 기본 캡처 모드를 사용한 다음 특정 변수에 대해 반대 모드를 명시적으로 지정할 수 있습니다. 예를 들어 람다 본문이 참조로 외부 변수 total에 액세스하고 값으로 외부 변수 인수에 액세스하는 경우 다음 캡처 절은 동일합니다.
[&total, factor] [factor, &total] [&, factor] [factor, &] [=, &total] [&total, =]
캡처 사용 -default , 람다에 언급된 변수만 캡처됩니다.
capture 절에 Capture-default&가 포함된 경우 해당 캡처 절의 식별자에 있는 캡처는 & 식별자 형식을 취할 수 없습니다. 마찬가지로, 캡처 절에 캡처-기본값=이 포함된 경우 캡처 절의 캡처는 = 식별자 형식을 취할 수 없습니다. 식별자 또는 이는 캡처 절에 두 번 이상 나타날 수 없습니다. 다음 코드 조각은 몇 가지 예를 제공합니다.
struct S { void f(int i); }; void S::f(int i) { [&, i]{}; // OK [&, &i]{}; // ERROR: i preceded by & when & is the default [=, this]{}; // ERROR: this when = is the default [i, i]{}; // ERROR: i repeated }
다음 가변 템플릿 예에 표시된 것처럼 줄임표가 뒤따르는 캡처는 패키지 확장입니다.
template<class... Args> void f(Args... args) { auto x = [args...] { return g(args...); }; x(); }
클래스 메서드 본문에서 람다 식을 사용하려면 이 포인터를 Capture 절에 전달하여 바깥쪽 클래스의 메서드와 데이터 멤버에 대한 액세스를 제공하세요. 클래스 메서드와 함께 람다 식을 사용하는 방법을 보여주는 예는 람다 식 예제의 "예: 메서드에서 람다 식 사용"을 참조하세요.
capture 절을 사용할 때는 다음 사항을 기억하는 것이 좋습니다(특히 여러 스레드를 사용하는 람다를 사용하는 경우).
참조 캡처를 사용하여 외부 변수를 수정할 수 있지만 값 캡처로는 이 작업을 수행할 수 없습니다. (변경 가능 항목은 복사본 수정을 허용하지만 원본 수정은 허용하지 않습니다.)
참조 캡처는 외부 변수에 대한 업데이트를 반영하지만 값 캡처는 반영하지 않습니다.
참조 캡처에는 수명 종속성이 도입되는 반면, 값 캡처에는 수명 종속성이 없습니다. 이는 람다가 비동기적으로 실행될 때 특히 중요합니다. 비동기 람다에서 참조로 지역 변수를 캡처하는 경우 람다가 실행될 때 지역 변수가 사라져 런타임 액세스 위반이 발생할 가능성이 높습니다.
유니버설 캡처(C++14)
C++14에서는 이러한 변수가 람다 함수의 바깥쪽 범위에 존재하지 않고도 Capture 절에서 새 변수를 도입하고 초기화할 수 있습니다. 초기화는 임의의 표현식으로 표현할 수 있으며 새 변수의 유형은 해당 표현식에 의해 생성된 유형에서 추론됩니다. 이 기능의 한 가지 이점은 C++14에서 이동 전용 변수(예: std::unique_ptr)를 주변 범위에서 캡처하여 람다에서 사용할 수 있다는 것입니다.
pNums = make_unique<vector<int>>(nums); //... auto a = [ptr = move(pNums)]() { // use ptr };
매개변수 목록
람다는 변수 캡처 외에도 입력 매개변수도 허용할 수 있습니다. 인수 목록(표준 구문에서는 람다 선언자라고 함)은 선택 사항이며 대부분의 측면에서 함수의 인수 목록과 유사합니다.
int y = [] (int first, int second) { return first + second; };
C++14에서는 매개변수 유형이 일반형인 경우 auto 키워드를 유형 지정자로 사용할 수 있습니다. 이는 컴파일러에게 함수 호출 연산자를 템플릿으로 생성하도록 지시합니다. 매개변수 목록에 있는 각 auto 인스턴스는 다른 유형 매개변수와 동일합니다.
아아아아lambda 表达式可以将另一个 lambda 表达式作为其参数。 有关详细信息,请参阅 Lambda 表达式的示例主题中的“高阶 Lambda 表达式”。
由于参数列表是可选的,因此在不将参数传递到 lambda 表达式,并且其 lambda-declarator: 不包含 exception-specification、trailing-return-type 或 mutable 的情况下,可以省略空括号。
可变规范
通常,lambda 的函数调用运算符为 const-by-value,但对 mutable 关键字的使用可将其取消。 它不会生成可变的数据成员。 利用可变规范,lambda 表达式的主体可以修改通过值捕获的变量。 本文后面的一些示例将显示如何使用 mutable。
异常规范
你可以使用 throw() 异常规范来指示 lambda 表达式不会引发任何异常。 与普通函数一样,如果 lambda 表达式声明 C4297 异常规范且 lambda 体引发异常,Visual C++ 编译器将生成警告 throw(),如下所示:
// throw_lambda_expression.cpp // compile with: /W4 /EHsc int main() // C4297 expected { []() throw() { throw 5; }(); }
返回类型
将自动推导 lambda 表达式的返回类型。 无需使用 auto 关键字,除非指定尾随返回类型。 trailing-return-type 类似于普通方法或函数的返回类型部分。 但是,返回类型必须跟在参数列表的后面,你必须在返回类型前面包含 trailing-return-type 关键字 ->。
如果 lambda 体仅包含一个返回语句或其表达式不返回值,则可以省略 lambda 表达式的返回类型部分。 如果 lambda 体包含单个返回语句,编译器将从返回表达式的类型推导返回类型。 否则,编译器会将返回类型推导为 void。 下面的代码示例片段说明了这一原则。
auto x1 = [](int i){ return i; }; // OK: return type is int auto x2 = []{ return{ 1, 2 }; }; // ERROR: return type is void, deducing // return type from braced-init-list is not valid
lambda 表达式可以生成另一个 lambda 表达式作为其返回值。 有关详细信息,请参阅 Lambda 表达式的示例中的“高阶 Lambda 表达式”。
Lambda 体
lambda 表达式的 lambda 体(标准语法中的 compound-statement)可包含普通方法或函数的主体可包含的任何内容。 普通函数和 lambda 表达式的主体均可访问以下变量类型:
从封闭范围捕获变量,如前所述。
参数
本地声明变量
类数据成员(在类内部声明并且捕获 this 时)
具有静态存储持续时间的任何变量(例如,全局变量)
以下示例包含通过值显式捕获变量 n 并通过引用隐式捕获变量 m 的 lambda 表达式:
// captures_lambda_expression.cpp // compile with: /W4 /EHsc #include <iostream> using namespace std; int main() { int m = 0; int n = 0; [&, n] (int a) mutable { m = ++n + a; }(4); cout << m << endl << n << endl; }
输出:
5 0
由于变量 n 是通过值捕获的,因此在调用 lambda 表达式后,变量的值仍保持 0 不变。 mutable 规范允许在 lambda 中修改 n。
尽管 lambda 表达式只能捕获具有自动存储持续时间的变量,但你可以在 lambda 表达式的主体中使用具有静态存储持续时间的变量。 以下示例使用 generate 函数和 lambda 表达式为 vector 对象中的每个元素赋值。 lambda 表达式将修改静态变量以生成下一个元素的值。
void fillVector(vector<int>& v) { // A local static variable. static int nextValue = 1; // The lambda expression that appears in the following call to // the generate function modifies and uses the local static // variable nextValue. generate(v.begin(), v.end(), [] { return nextValue++; }); //WARNING: this is not thread-safe and is shown for illustration only }
下面的代码示例使用上一示例中的函数,并添加了使用 STL 算法 generate_n 的 lambda 表达式的示例。 该 lambda 表达式将 vector 对象的元素指派给前两个元素之和。 使用了 mutable 关键字,以使 lambda 表达式的主体可以修改 lambda 表达式通过值捕获的外部变量 x 和 y 的副本。 由于 lambda 表达式通过值捕获原始变量 x 和 y,因此它们的值在 lambda 执行后仍为 1。
// compile with: /W4 /EHsc #include#include #include #include using namespace std; template void print(const string& s, const C& c) { cout << s; for (const auto& e : c) { cout << e << " "; } cout << endl; } void fillVector(vector<int>& v) { // A local static variable. static int nextValue = 1; // The lambda expression that appears in the following call to // the generate function modifies and uses the local static // variable nextValue. generate(v.begin(), v.end(), [] { return nextValue++; }); //WARNING: this is not thread-safe and is shown for illustration only } int main() { // The number of elements in the vector. const int elementCount = 9; // Create a vector object with each element set to 1. vector v(elementCount, 1); // These variables hold the previous two elements of the vector. int x = 1; int y = 1; // Sets each element in the vector to the sum of the // previous two elements. generate_n(v.begin() + 2, elementCount - 2, [=]() mutable throw() -> int { // lambda is the 3rd parameter // Generate current value. int n = x + y; // Update previous two values. x = y; y = n; return n; }); print("vector v after call to generate_n() with lambda: ", v); // Print the local variables x and y. // The values of x and y hold their initial values because // they are captured by value. cout << "x: " << x << " y: " << y << endl; // Fill the vector with a sequence of numbers fillVector(v); print("vector v after 1st call to fillVector(): ", v); // Fill the vector with the next sequence of numbers fillVector(v); print("vector v after 2nd call to fillVector(): ", v); }
输出:
vector v after call to generate_n() with lambda: 1 1 2 3 5 8 13 21 34 x: 1 y: 1 vector v after 1st call to fillVector(): 1 2 3 4 5 6 7 8 9 vector v after 2nd call to fillVector(): 10 11 12 13 14 15 16 17 18
更多C++11의 새로운 기능과 결합하여 C++에서 람다 식의 사용법을 학습합니다.相关文章请关注PHP中文网!