다음을 통해 공유


람다 식 구문

이 문서에서는 람다 식의 구문과 구성 요소에 대해 설명합니다. 람다 식에 대한 설명은 C++의 람다 식을 참조하세요.

람다 식 문법

ISO C++11 표준의 다음 정의는 람다 식의 문법을 나타냅니다. opt 첨자가 표시된 항목은 선택 항목입니다.

        lambda-introducer lambda-declaratoropt compound-statement

다음의 구문 구성 요소가 추가로 지정됩니다.

lambda-introducer:
        [ lambda-captureopt ]
lambda-capture:
        capture-default
        capture-list
        capture-default , capture-list
capture-default:
        &
        =
capture-list:
        capture ...opt
        capture-list , capture ...opt
capture:
        식별자
        & 식별자
        this
lambda-declarator:
        ( parameter-declaration-clause ) mutableopt
                exception-specificationopt attribute-specifier-seqopt trailing-return-typeopt

Visual Studio에서는 C++11 표준 람다 식 구문 및 기능이 지원되지만 다음과 같은 예외 사항이 있습니다.

  • 람다는 다른 모든 클래스와 마찬가지로 이동 생성자 및 이동 할당 연산자를 자동으로 생성하지 않습니다. rvalue 참조 동작 지원에 대한 자세한 내용은 C++11 기능에 대한 지원(최신 C++)의 "Rvalue 참조" 섹션을 참조하세요.

  • 선택 항목인 attribute-specifier-seq가 이 버전에서 지원되지 않습니다.

Visual Studio에는 C++11 표준 람다 기능 이외에 다음과 같은 기능이 있습니다.

  • 상태 비저장 람다를 통해 임의의 호출 규칙을 사용하는 함수 포인터로의 모든 변환이 가능합니다.

  • 모든 return 문의 형식이 같은 한 { return expression; }보다 복잡한 람다 본문에 대해 반환 형식이 자동으로 추론됩니다. 이 기능은 제안된 C++14 표준에 포함되어 있습니다.

람다 식의 속성

다음 그림에서는 문법을 예제에 매핑합니다.

Structural elements of a lambda expression

  1. lambda-introducer(캡처 절이라고도 함)

  2. lambda declarator(매개 변수 목록이라고도 함)

  3. mutable(변경 가능한 사양이라고도 함)

  4. exception-specification(예외 사양이라고도 함)

  5. trailing-return-type(반환 형식이라고도 함)

  6. compound-statement(람다 본문이라고도 함)

캡처 절

람다 식은 기본적으로 클래스, 생성자 및 함수 호출 연산자입니다. 클래스를 정의하는 경우와 마찬가지로 람다에서는 결과 개체가 값 또는 참조로 변수를 캡처할지 아니면 아무 것도 캡처하지 말아야 하는지 결정해야 합니다. 람다 식이 지역 변수 및 함수 매개 변수에 액세스해야 하는 경우 캡처되어야 합니다. 캡처 절(표준 구문의 lambda-introducer)은 람다 식의 본문이 값이나 참조로 바깥쪽 범위의 변수에 액세스할 수 있는지를 지정합니다. 앰퍼샌드(&) 접두사가 있는 변수는 참조로 액세스되며 이 접두사가 없는 변수는 값으로 액세스됩니다.

빈 캡처 절인 [ ]는 람다 식의 본문이 바깥쪽 범위의 변수에 액세스하지 않음을 나타냅니다.

기본 캡처 모드(표준 구문의 capture-default)를 사용하여, 지정되지 않은 변수를 값이나 참조로 캡처할 수 있습니다. & 또는 =를 캡처 절의 첫 요소로 사용하여 기본 캡처 모드를 지정합니다. & 요소는 람다 식의 본문이 지정되지 않은 변수에 참조로 액세스함을 나타냅니다. = 요소는 람다 본문이 지정되지 않은 변수에 값으로 액세스함을 나타냅니다. 예를 들어, 람다 본문이 외부 변수 total에 참조별로 액세스하고 외부 변수 factor에 값별로 액세스하는 경우 다음 캡처 절이 동일합니다.

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

capture-default에 대한 일반적인 오해는 범위에 있는 모든 변수가 람다에서 사용되는지와 관계없이 캡처된다는 것입니다. 사실은 그렇지 않습니다. capture-default를 사용할 때는 람다에 언급된 변수만 캡처됩니다.

캡처 절에 capture-default &가 포함된 경우 해당 캡처 절의 capture에서 identifier는 & identifier 형태가 될 수 없습니다. 마찬가지로, 캡처 절에 capture-default =가 포함된 경우 해당 캡처 절의 capture는 = identifier 형태가 될 수 없습니다. 식별자 또는 this는 캡처 절에 두 번 이상 나타날 수 없습니다. 다음 코드 조각은 몇 가지 사례를 보여 줍니다.

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
}

다음의 variadic 템플릿 예제에서와 같이, 줄임표 앞에 오는 capture는 팩 확장입니다.

template<class... Args>
void f(Args... args) {
    auto x = [args...] { return g(args...); };
    x();
}

클래스 메서드의 본문에 람다 식을 사용할 수 있습니다. this 포인터를 캡처 절에 전달하여 바깥쪽 클래스의 메서드 및 데이터 멤버에 대한 액세스를 제공합니다. 클래스 메서드에 람다 식을 사용하는 방법을 보여 주는 예제는 람다 식의 예의 "예제: 메서드에 람다 식 사용"을 참조하십시오.

캡처 절을 사용하는 경우 특히 람다를 다중 스레딩과 함께 사용할 때는 다음과 같은 사항에 유의해야 합니다.

  • 참조 캡처는 외부 변수를 수정하는 데 사용할 수 있지만 값 캡처는 사용할 수 없습니다(mutable을 통해 복사본은 수정되지만 원본은 수정되지 않습니다.)

  • 참조 캡처는 외부 변수에 대한 업데이트를 반영하지만 값 캡처는 반영하지 않습니다.

  • 참조 캡처는 수명 종속성을 발생시키지만 값 캡처에는 수명 종속성이 없습니다.

매개 변수 목록

매개 변수 목록(표준 구문의 lambda declarator)은 선택 사항이며 함수의 매개 변수 목록과 유사합니다.

람다 식은 다른 람다 식을 인수로 사용할 수 있습니다. 자세한 내용은 람다 식의 예 항목의 "고차 람다 식"을 참조하십시오.

매개 변수 목록은 선택 사항이므로 람다 식에 인수를 전달하지 않고 lambda-declarator:에 exception-specification, trailing-return-type 또는 mutable이 포함되지 않는 경우 빈 괄호를 생략할 수 있습니다.

변경 가능한 사양

일반적으로 람다 함수 호출 연산자는 값 단위 상수이지만 mutable 키워드를 사용할 경우 이를 취소합니다. 변경 가능 데이터 멤버가 생성되지 않습니다. 변경 가능한 사양을 사용하면 람다 식의 본문이 값별로 캡처되는 변수를 수정할 수 있습니다. 이 문서의 뒷부분에 나오는 몇 가지 예제는 mutable 키워드 사용 방법에 대해 설명합니다.

예외 사양

람다 식이 어떠한 예외도 throw하지 않음을 나타내기 위해 throw() 예외 사양을 사용할 수 있습니다. 여기에서 볼 수 있는 것과 같이, 일반 함수와 마찬가지로 람다 식이 throw() 예외 사양을 선언하고 람다 본문이 예외를 throw하는 경우 Visual C++ 컴파일러는 C4297 경고를 생성합니다.

// throw_lambda_expression.cpp
// compile with: /W4 /EHsc 
int main() // C4297 expected
{
   []() throw() { throw 5; }();
}

자세한 내용은 예외 사양을 참조하십시오.

반환 형식

람다 식의 반환 형식은 자동으로 추론됩니다. trailing-return-type을 지정하지 않는 한 auto 키워드를 표시할 필요가 없습니다. trailing-return-type은 일반 메서드 또는 함수의 반환 형식 부분과 유사합니다. 그러나 반환 형식은 매개 변수 목록 뒤에 와야 하며 반환 형식 앞에 trailing-return-type 키워드 ->를 포함해야 합니다.

람다 본문에 return 문이 하나만 포함되거나 식이 값을 반환하지 않으면 람다 식의 반환 형식 부분을 생략할 수 있습니다. 람다 본문에 단일 return 문이 포함되어 있으면 컴파일러는 반환 식의 형식에서 반환 형식을 추론합니다. 그렇지 않으면 컴파일러는 반환 형식이 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

람다 식은 다른 람다 식을 반환 값으로 생성할 수 있습니다. 자세한 내용은 람다 식의 예의 "고차 람다 식"을 참조하십시오.

람다 본문

람다 식의 람다 본문(표준 구문의 compound-statement)에는 일반 메서드 또는 함수 본문에 포함할 수 있는 모든 항목을 포함할 수 있습니다. 일반 함수와 람다 식 모두의 본문은 다음과 같은 종류의 변수에 액세스할 수 있습니다.

  • 매개 변수

  • 로컬로 선언된 변수

  • 클래스 데이터 멤버(클래스 내부에서 선언되고 this가 캡처된 경우)

  • 정적 저장 기간이 있는 모든 변수(예: 전역 변수)

또한 람다 식은 바깥쪽 범위에서 캡처하는 변수에 액세스할 수 있습니다. 변수가 람다 식의 캡처 절에 나타나면 변수가 명시적으로 캡처됩니다. 그렇지 않으면 변수가 암시적으로 캡처됩니다. 람다 식의 본문은 기본 캡처 모드를 사용하여 암시적으로 캡처되는 변수에 액세스합니다.

다음 예제에는 n 변수를 값별로 명시적으로 캡처하고 m 변수를 참조별로 암시적으로 캡처하는 람다 식이 포함되어 있습니다.

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

출력:

  

n 변수는 값별로 캡처되므로 람다 식을 호출한 후 해당 값이 0으로 유지됩니다. mutable 사양을 사용하면 람다 내부에서 n을 수정할 수 있습니다.

람다 식은 자동 저장 기간이 있는 변수만 캡처할 수 있지만 람다 식의 본문에서는 정적 저장 기간이 있는 변수를 사용할 수 있습니다. 다음 예제는 generate 함수 및 람다 식을 사용하여 vector 개체의 각 요소에 값을 할당합니다. 람다 식은 정적 변수를 수정하여 다음 요소의 값을 생성합니다.

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을 사용하는 람다 식의 예제를 추가합니다. 이 람다 식은 vector 개체의 요소를 이전의 두 요소의 합에 할당합니다. 람다 식의 본문이 외부 변수 x 및 y(람다 식이 값별로 캡처함)의 복사본을 수정할 수 있도록 mutable 키워드가 사용됩니다. 람다 식이 원래 변수 x 및 y를 값별로 캡처하기 때문에 람다가 실행된 후에도 값이 1로 유지됩니다.

// compile with: /W4 /EHsc
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>

using namespace std;

template <typename C> 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<int> 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);
}

출력:

  

자세한 내용은 generate_n을 참조하십시오.

Microsoft 전용 한정자

__declspec 등의 Microsoft 전용 한정자를 사용할 경우 다음 예와 같이 람다 식에서 parameter-declaration-clause 직후에 삽입할 수 있습니다.

auto Sqr = [](int t) __declspec(code_seg("PagedMem")) -> int { return t*t; };

람다에서의 한정자 지원 여부를 확인하려면 설명서의 Microsoft 전용 한정자 섹션에서 해당 문서를 참조하세요.

참고 항목

참조

C++의 람다 식

람다 식의 예

생성

generate_n

for_each

예외 사양

컴파일러 경고 (수준 1) C4297

Microsoft 전용 한정자