Lambda expression을 설명하기에 앞서 auto 키워드를 간략히 다뤄보겠습니다.
컴파일러가 변수의 타입을 자동으로 추론하도록 해주는 기능입니다.
auto x = 10; // x는 int로 추론
auto y = 3.14; // y는 double로 추론
auto z = "Hello"; // z는 const char *로 추론
auto [a, b] = make_pair(3, 4.5); // a는 int로 추론, b는 double로 추론
auto a; // Error
컴파일러가 예상치 못한 타입으로 추론하여 의도한 것과 다른 타입이 될 수 있고, 타입 정보가 숨겨져 있어서 코드의 명시성이 떨어질 수 있습니다.
주로 함수의 인수로 호출되거나 전달되는 위치에서 익명 함수(Anonymous Function) 또는 람다 함수(Lambda Function)를 정의하는 편리한 방법으로, 이름이 없는 함수 객체를 간단하고 직관적인 방식으로 표현합니다. 이는 일회성 함수 정의가 필요할 때 유용하게 사용됩니다.
람다 표현식
[Capture](Parameters) mutable Exception-Specification -> Return type { Statement };
주로 사용되는 람다식
[Capture](Parameters) { Statement };
쉬운 예시
#include <iostream>
using namespace std;
int main() {
// 람다 함수 정의
auto add = [](int x, int y) { return x + y; };
// 람다 함수 호출
cout << "3 + 5 = " << add(3, 5) << endl; // 3 + 5 = 8
return 0;
}
인자로 사용되는 람다식
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> numbers = {10, 2, 3, 15, 7};
// 람다를 사용하여 내림차순으로 정렬
sort(numbers.begin(), numbers.end(), [](int a, int b) { return a > b; });
// 정렬된 결과 출력
for (const int & num : numbers) {
cout << num << " "; // 15 10 7 3 2
}
return 0;
}
람다 함수의 타입은 컴파일 타임에 결정되므로, auto를 사용하여 타입을 자동으로 추론하도록 하는 것이 매우 편리합니다. 이를 통해 람다의 타입을 명시적으로 지정할 필요 없이 간단하게 사용할 수 있습니다.
auto로 선언
#include <iostream>
using namespace std;
int main() {
auto lambda = [](int a, int b) { return a + b; };
return 0;
}
명시적으로 타입 지정
#include <iostream>
#include <functional> // std::function 사용을 위한 헤더
using namespace std;
int main() {
function<int(int, int)> lambda = [](int a, int b) { return a + b; };
// 단순한 int형이 아님.
return 0;
}
람다 표현식에서 외부 변수를 람다 함수 내부로 가져오는 방식을 말합니다.
Capture 종류
외부 변수 x, y
캡처하지 않음 '[]' : 모든 외부 변수를 캡처하지 않음을 의미합니다. 즉, 람다 함수 내부에서 외부 변수에 접근할 수 없으며, 오로지 람다 내부에서 정의된 변수들만 사용할 수 있습니다.
값으로 캡처(By Value) '[x]' : 외부 변수를 값으로 캡처하면, 람다 함수가 호출될 때 변수의 값이 복사되어 람다 내부에서 사용됩니다. 이 경우, 외부 변수가 변경되어도 람다 내부의 값은 변경되지 않으며 않습니다.
참조로 캡처(By Reference) '[&x]' : 외부 변수를 참조로 캡처하면, 람다 함수가 호출될 때 변수의 값이 참조되어 람다 내부에서 사용됩니다. 즉, 람다 내부에서 해당 변수를 수정하면 외부 변수에도 영향을 미칩니다.
모든 변수를 값으로 캡처 '[=]' : 모든 외부 변수를 값으로 캡처합니다.
모든 변수를 참조로 캡처 '[&]' : 모든 외부 변수를 참조로 캡처합니다.
혼합 캡처 '[x, &y]' : 일부 변수는 값으로, 일부 변수는 참조로 캡처합니다.
#include <iostream>
using namespace std;
int main() {
int x = 5, y = 10;
auto lambda1 = []() { cout << "No Capture" << endl; };
auto lambda2 = [x]() { cout << "Captured by value : " << x << endl; };
auto lambda3 = [&x]() { cout << "Captured by reference : " << x << endl; };
auto lambda4 = [=]() { cout << "All captured by value : " << x << ", " << y << endl; };
auto lambda5 = [&]() { cout << "All captured by reference : " << x << ", " << y << endl; };
auto lambda6 = [x, &y]() { cout << "x is captured by value : " << x << "/n y is captured by reference : " << y << endl; };
return 0;
}
C++ 람다 함수에서 '값으로 캡처된 변수의 값'을 수정할 수 있게 해주는 특수한 키워드입니다.
기본적으로 값으로 캡처된 외부 변수는 읽기 전용으로 취급되기 때문에, 람다 함수 내부에서 값을 변경할 수 없습니다.
하지만 mutable 키워드를 사용하면, '값으로 캡처된 변수'에 한하여 값을 수정할 수 있도록 const 제약을 해제해주는 역할을 합니다.
#include <iostream>
using namespace std;
int main() {
int x = 10;
auto lambda1 = [x]() { // x를 값으로 캡처
x += 5; // Error : x는 읽기 전용이라 수정할 수 없음
};
auto lambda2 = [x]() mutable {
x += 5;
cout << x << endl; // 출력 : 15
};
lambda2();
cout << "After running lambda2 x : " << x << endl; // 출력 : 10
auto lambda3 = [&x]() { // 참조로 캡처하였기 때문에 x는 읽기 전용이 아님
x += 5;
};
lambda3();
cout << "After running lambda3 x : " << x << endl; // 출력 : 15
return 0;
}
함수가 예외를 던질 수 있는지 여부를 명시하는 방식입니다. 기존의 throw 기반 예외 사양은 C++11에서 noexcept로 대체되었으며, 이는 함수가 예외를 던지지 않음을 보장하는 방식입니다.
컴파일러가 이를 컴파일 오류로 처리합니다. 이런 상황에서는 프로그램이 실행되기 전에 컴파일 단계에서 오류가 발생합니다.
이 경우 컴파일 타임에 예외가 던져질 수 없다는 것이 보장되기 때문에 예외가 발생할 경우 std::terminate() 함수가 호출되어 프로그램이 강제 종료됩니다.