C++에서 **람다(lambda)**는 **익명 함수(anonymous function)**를 정의하는 기능으로, 간결한 코드 작성과 함수 객체를 쉽게 만들 수 있도록 도와줍니다. C++11부터 도입되었으며, C++14, C++17, C++20을 거치며 더 강력하게 발전했습니다.
람다 기본 문법
[캡처](매개변수) -> 반환형 { 함수 본문 };
- [캡처] : 외부 변수 캡처 방식 지정 (&, =, 특정 변수 선택 가능)
- (매개변수) : 함수의 매개변수
- -> 반환형 : 반환형 (생략 가능, 자동 추론됨)
- { 함수 본문 } : 실행할 코드
람다 기본 사용 예제
#include <iostream>
int main() {
// 기본적인 람다 표현식
auto add = [](int a, int b) { return a + b; };
std::cout << "3 + 5 = " << add(3, 5) << std::endl; // 출력: 3 + 5 = 8
return 0;
}
캡처 방식
람다는 외부 변수를 캡처하여 사용할 수 있습니다.
1. 값 캡처 (=)
#include <iostream>
int main() {
int x = 10;
auto lambda = [=]() { std::cout << "x = " << x << std::endl; };
lambda(); // 출력: x = 10
}
2. 참조 캡처 (&)
#include <iostream>
int main() {
int x = 10;
auto lambda = [&]() { x += 5; };
lambda();
std::cout << "x = " << x << std::endl; // 출력: x = 15
}
3. 특정 변수만 캡처
#include <iostream>
int main() {
int a = 10, b = 20;
auto lambda1 = [a]() { std::cout << "a = " << a << std::endl; }; // a만 값 캡처
auto lambda2 = [&b]() { b += 5; }; // b만 참조 캡처
lambda1(); // 출력: a = 10
lambda2();
std::cout << "b = " << b << std::endl; // 출력: b = 25
}
4. 반환형 지정
#include <iostream>
int main() {
auto divide = [](double a, double b) -> double {
if (b == 0) return 0.0;
return a / b;
};
std::cout << "10 / 2 = " << divide(10, 2) << std::endl; // 출력: 10 / 2 = 5
}
5. mutable 키워드
람다에서 [=] 또는 [변수]를 사용하여 값 캡처를 하면, 해당 변수는 읽기 전용이 됩니다. 즉, 람다 내부에서 값을 변경할 수 없습니다. 하지만 mutable 키워드를 사용하면 값 캡처된 변수를 변경할 수 있습니다. 단, 이는 원래 변수에는 영향을 미치지 않습니다.
#include <iostream>
int main() {
int x = 10;
auto lambda = [x]() mutable {
x += 5;
std::cout << "x in lambda = " << x << std::endl;
};
lambda();
std::cout << "x outside lambda = " << x << std::endl;
return 0;
}
출력:
x in lambda = 15
x outside lambda = 10
- mutable을 사용하면 캡처된 변수의 복사본을 변경할 수 있지만, 원래 변수에는 영향을 주지 않습니다.
6. 람다를 함수 인자로 전달
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::for_each(numbers.begin(), numbers.end(), [](int n) {
std::cout << n * 2 << " ";
});
}
7. 람다와 std::function
#include <iostream>
#include <functional>
int main() {
std::function<int(int, int)> add = [](int a, int b) { return a + b; };
std::cout << "10 + 20 = " << add(10, 20) << std::endl;
}
8. 람다 사용 시 주의할 점
캡처한 변수의 생존 기간
#include <iostream>
#include <functional>
std::function<void()> getLambda() {
int x = 10;
return [&]() { std::cout << x << std::endl; }; // 위험!
}
int main() {
auto lambda = getLambda();
lambda(); // x는 이미 소멸되어 undefined behavior 발생 가능!
}
mutable 없이 값 캡처 변수 수정 불가
int x = 10;
auto lambda = [x]() { x += 5; }; // 오류 발생 (x는 읽기 전용)
람다의 크기 문제
- 람다는 내부적으로 함수 객체 (클래스)로 변환되므로, 캡처된 변수만큼 메모리를 차지합니다.
- 불필요한 캡처는 피하는 것이 좋습니다.
Q&A
Q1: 람다에서 mutable을 언제 사용해야 하나요?
A: 값 캡처된 변수를 람다 내부에서 수정해야 할 때 사용합니다. 단, 원래 변수에는 영향을 주지 않습니다.
Q2: 람다에서 참조 캡처를 사용할 때 주의해야 할 점은?
A: 참조 캡처한 변수는 원래 변수의 수명에 의존하므로, 함수가 끝난 후 접근하면 dangling reference 오류가 발생할 수 있습니다.
Q3: 람다는 어느 정도의 오버헤드가 발생하나요?
A: 람다는 내부적으로 함수 객체로 변환되며, 캡처하는 변수에 따라 크기가 증가할 수 있습니다. 따라서 불필요한 캡처는 피하는 것이 좋습니다.
결론
- C++ 람다는 코드의 간결성과 가독성을 높이는 강력한 도구입니다.
- 하지만 캡처 방식과 변수의 생명 주기를 신경 써야 합니다.
- 특히, 참조 캡처는 dangling reference 문제를 유발할 수 있으므로 주의해야 합니다! 🚀