>  기사  >  백엔드 개발  >  C++11의 새로운 기능과 결합하여 C++에서 람다 식의 사용법을 학습합니다.

C++11의 새로운 기능과 결합하여 C++에서 람다 식의 사용법을 학습합니다.

高洛峰
高洛峰원래의
2017-01-23 13:57:091543검색

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
 );
}

이 다이어그램은 람다의 구성 요소를 보여줍니다.

C++11의 새로운 기능과 결합하여 C++에서 람다 식의 사용법을 학습합니다.

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中文网!   


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.