로또
람다 함수 본문
작성 이유
알고리즘을 풀거나 javascript를 사용할 때 자주 등장하는 람다 함수에 대해 자세히 알고 싶었다.
정의
함수에 인수로 호출되거나 전달되는 위치에서 바로 익명 함수 개체(클로저)를 정의하는 방법.
구조
[<Capture>] (<Parameter>) <mutable> <exception-specification> -> <trailing-return-type>
{
lambda body
}
<Capture>
람다는 본문에 새 변수를 도입하거나 주변 범위의 변수에 접근하기 위해 Capture를 사용할 수 있다.
람다는 Capture절로 시작하는데, 캡처되는 변수가 값에 의한 것인지, 참조에 의한 것인지 지정할 수 있다.

Capture에 변수명을 다음과 같이 넣으면, 값을 복사하여 람다 함수가 호출된다.
int main(){
string s = "main func value";
cout << "In main, s Address: " << &s << endl;
[s](){
cout << "In Lambda, s Address: " << &s << endl;
}();
}

하지만 다음과 같이 & 연산자를 붙인다면, 값을 참조하여 람다 함수가 호출됨을 확인할 수 있다.
int main(){
string s = "main func value";
cout << "In main, s Address: " << &s << endl;
[&s](){
cout << "In Lambda, s Address: " << &s << endl;
}();
}

[=]: 모든 외부 변수를 값에 의해 복사하여 람다 함수 내에서 사용할 수 있다.
[&]: 모든 외부 변수를 참조하여 람다 함수 내에서 사용할 수 있다.
<Parameter>
함수를 호출할 때 필요한 Parameter를 담는다.
int main(){
[](int a, int b){
cout << "Param a: " << a << endl;
cout << "Param b: " << b << endl;
}(2, 3);
}

<Mutable>
기본적으로 Capture한 변수는 Const로 설정된다.
때문에 Capture한 변수를 수정하게 되면 오류가 발생한다.


하지만, Mutable을 사용하면 Capture한 값을 수정 가능하게 만들기 때문에 오류가 발생하지 않는다.
int main()
{
int captured = 10;
[captured] (int param) mutable{
captured++;
param++;
cout << captured << endl;
cout << param << endl;
}(5);
}
<Exception-specificion>
일반 함수에서와 동일하게 함수에서 발생할 수 있는 예외를 명시할 수 있다.
int main() {
auto lambda = []() {
cout << "Inside lambda" <<endl;
throw runtime_error("Exception inside lambda");
};
try {
lambda();
} catch (exception e) {
cerr << "Caught exception: " << e.what() <<endl;
}
return 0;
}
lambda 함수 내에서 발생한 예외는 lambda함수를 호출한 상위 호출자에 전달된다.

예를 들어, noexcept 는 해당 함수에서는 예외가 발생하지 않음을 나타낸다. 또한 예외가 발생하더라도 상위 호출자에 예외가 전달되지 않는다.
int main() {
auto lambda = []() noexcept{
cout << "Inside noexcept lambda" <<endl;
throw runtime_error("Exception inside noexcept lambda");
};
try {
lambda();
} catch (exception e) {
cerr << "Caught exception: " << e.what() <<endl;
}
return 0;
}

<Trailing-return-type>
번역하면 후행 반환 형식이다.
auto 로 정의된 함수의 return type을 명시적으로 알려주는 역할을 한다. 때문에 함수의 return type을 auto로 지정하지 않으면 사용할 필요가 없다.
auto 를 return하도록 정의된 람다 함수(lambda_func, lambda_func2)의 경우에 단일 return 식이라면 컴파일러는 반환 형식을 추론할 수 있지만 그렇지 않으면 컴파일러는 return type을 void로 추론하기 때문에 오류가 발생한다.

다음과 같이, lambda_func3에 trailing-return-type으로 pair<int, int> 를 명시해주면 오류가 사라진다.
int main() {
auto lambda_func = [](int a, int b) {
return make_pair(a, b);
};
auto lambda_func2 = [](int a){
return a;
};
auto lambda_func3 = []() -> pair<int, int>{
return {1, 2};
};
auto v1 = lambda_func(10, 20);
cout << v1.first << ", " << v1.second << endl;
auto v2 = lambda_func2(2);
cout << v2 << endl;
auto tmp = lambda_func3();
cout << tmp.first << ", " << tmp.second << endl;
return 0;
}

정리
Capture를 통해 외부 변수를 값이나 참조 형식으로 받아와 사용할 수 있다. [=]이나 [&]를 사용하면 외부 변수를 하나하나 명시하지 않아도 값이나 참조 형식으로 받아올 수 있다.
Parameter를 통해 인자를 전달받을 수 있다.
기본적으로 Capture를 통해 받은 변수는 Const 형태이지만 Mutable을 사용하면 Const 속성을 제거할 수 있다.
다른 일반 함수처럼 발생 가능한 예외를 명시할 수 있으며, noexcept를 사용하면 예외가 상위로 전달되지 않는다.
auto를 return-type으로 지정하면서 단일 return문이 아니라면, trailing return type을 명시해주어야 한다.
int main() {
string s = "Main String";
[&s](int a) mutable noexcept -> pair<int, int>{
cout << "paramter a: " << a << endl;
s = "Lambda String";
}(100);
cout << s << endl;
}
참고자료
https://learn.microsoft.com/ko-kr/cpp/cpp/lambda-expressions-in-cpp?view=msvc-170
C++ 람다 식
자세한 정보: C++의 람다 식
learn.microsoft.com
https://blockdmask.tistory.com/491
[C++] 람다 표현식, lambda에 대해서
안녕하세요. BlockDMask입니다. 오늘은 C++11, 14에서 추가된 lambda 표현식에 대해 알아보겠습니다. 1. 람다 표현식 2. 람다 표현식 사용 방법과 구조 3. 람다의 필요성, 사용 예제 1. C++ 람다 표현식 lambda
blockdmask.tistory.com
https://m.blog.naver.com/sjg03179/220680619548
[C++11] auto, decltype, trailing return type
auto 정의 : 값에 상응하는 형식을 [컴파일 타임]에 추론해 주는 키워드이다. vector<string>::const...
blog.naver.com