람다 식 구문
이 문서에서는 람다 식의 구문과 구성 요소에 대해 설명합니다. 람다 식에 대한 설명은 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 표준에 포함되어 있습니다.
람다 식의 속성
다음 그림에서는 문법을 예제에 매핑합니다.
lambda-introducer(캡처 절이라고도 함)
lambda declarator(매개 변수 목록이라고도 함)
mutable(변경 가능한 사양이라고도 함)
exception-specification(예외 사양이라고도 함)
trailing-return-type(반환 형식이라고도 함)
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 전용 한정자 섹션에서 해당 문서를 참조하세요.