람다 표현식
람다 표현식의 개념과 기본 구조
람다 표현식은 C++ 11에서 도입된 기능으로, 익명 함수를 생성하는 간결한 방법을 제공합니다.
이는 함수형 프로그래밍의 개념을 C++에 도입한 것으로, 코드를 더 간결하고 표현력 있게 만들어줍니다.
람다 표현식의 기본 구조는 다음과 같습니다.
[capture clause] (parameters) -> return_type { function body }
[capture clause]
: 람다 함수 외부의 변수를 캡처하는 방법을 지정(parameters)
: 함수의 매개변수 (선택적)-> return_type
: 반환 타입 (선택적, 대부분의 경우 컴파일러가 추론 가능){ function body }
: 함수의 본문
예를 들어, 다음은 두 정수를 더하는 간단한 람다 표현식입니다.
auto add = [](int a, int b) { return a + b; };
캡처 절 (Capture Clause)
캡처 절은 람다 함수가 외부 범위의 변수를 어떻게 "캡처"할지 지정합니다.
주요 캡처 방식은 다음과 같습니다.
[]
: 아무것도 캡처하지 않음[=]
: 모든 외부 변수를 값으로 캡처[&]
: 모든 외부 변수를 참조로 캡처[x]
: x를 값으로 캡처[&x]
: x를 참조로 캡처[=, &x]
: x만 참조로 캡처하고 나머지는 값으로 캡처[&, x]
: x만 값으로 캡처하고 나머지는 참조로 캡처
#include <iostream>
int main() {
int x = 10;
int y = 20;
auto lambda1 = [x, &y]() {
// x는 값으로 캡처되어 변경 불가
// y는 참조로 캡처되어 변경 가능
y += 5;
return x + y;
};
std::cout << "Result: " << lambda1() << std::endl; // 출력: Result: 35
std::cout << "y is now: " << y << std::endl; // 출력: y is now: 25
return 0;
}
람다와 STL 알고리즘
람다 표현식은 STL 알고리즘과 함께 사용될 때 특히 강력합니다.
예를 들어, std::sort
를 사용할 때 람다를 통해 사용자 정의 비교 함수를 쉽게 제공할 수 있습니다.
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {4, 1, 3, 5, 2};
// 내림차순 정렬
std::sort(numbers.begin(), numbers.end(), [](int a, int b) { return a > b; });
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl; // 출력: 5 4 3 2 1
return 0;
}
제네릭 람다 (C++ 14)
C++ 14부터는 람다의 매개변수 타입을 auto
로 지정할 수 있어, 제네릭 람다를 만들 수 있습니다.
이를 통해 타입에 독립적인 람다를 작성할 수 있습니다.
#include <iostream>
#include <vector>
#include <string>
int main() {
auto print = [](const auto& container) {
for (const auto& item : container) {
std::cout << item << " ";
}
std::cout << std::endl;
};
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::vector<std::string> words = {"Hello", "Generic", "Lambda"};
print(numbers); // 출력: 1 2 3 4 5
print(words); // 출력: Hello Generic Lambda
return 0;
}
람다와 클로저
람다 표현식은 클로저를 생성합니다. 클로저는 함수와 그 함수가 참조하는 환경을 포함하는 객체입니다.
이를 통해 함수가 정의된 환경의 상태를 "기억"할 수 있습니다.
#include <iostream>
#include <functional>
std::function<int(int)> makeMultiplier(int factor) {
return [factor](int x) { return x * factor; };
}
int main() {
auto times2 = makeMultiplier(2);
auto times3 = makeMultiplier(3);
std::cout << "5 * 2 = " << times2(5) << std::endl; // 출력: 5 * 2 = 10
std::cout << "5 * 3 = " << times3(5) << std::endl; // 출력: 5 * 3 = 15
return 0;
}
mutable 람다
기본적으로 람다는 값으로 캡처된 변수를 변경할 수 없습니다.
mutable
키워드를 사용하면 이를 가능하게 할 수 있습니다.
#include <iostream>
int main() {
int x = 10;
auto lambda = [x]() mutable {
x += 5;
return x;
};
std::cout << lambda() << std::endl; // 출력: 15
std::cout << lambda() << std::endl; // 출력: 20
std::cout << "Original x: " << x << std::endl; // 출력: Original x: 10
return 0;
}
람다의 성능 고려사항
람다는 일반적으로 인라인화되어 함수 포인터나 std::function보다 더 효율적입니다.
하지만 캡처하는 변수가 많아지면 성능에 영향을 줄 수 있습니다. 대규모 객체를 캡처할 때는 참조 캡처를 고려해볼 수 있습니다.
C++ 17 이후의 람다 기능
- C++ 17 : 람다 캡처에서
*this
의 복사 캡처 가능 - C++ 20 : 템플릿 람다, 캡처되지 않은 람다를 함수 포인터로 변환 가능
실습 : 학생 성적 처리 시스템
학생들의 성적 정보를 처리하는 시스템을 람다를 활용하여 구현해보세요.
- 학생 구조체 정의 (이름, 점수)
- 학생들의 벡터 생성
- 람다를 사용하여 다음 작업 수행
- 점수에 따라 학생 정렬
- 특정 점수 이상인 학생 필터링
- 모든 학생의 평균 점수 계산
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
struct Student {
std::string name;
int score;
};
int main() {
std::vector<Student> students = {
{"Alice", 85}, {"Bob", 70}, {"Charlie", 90}, {"David", 80}
};
// 점수에 따라 정렬
std::sort(students.begin(), students.end(),
[](const Student& a, const Student& b) { return a.score > b.score; });
std::cout << "Sorted students:" << std::endl;
for (const auto& student : students) {
std::cout << student.name << ": " << student.score << std::endl;
}
// 80점 이상인 학생 필터링
auto highScorers = std::count_if(students.begin(), students.end(),
[](const Student& s) { return s.score >= 80; });
std::cout << "\nNumber of students with score 80 or above: " << highScorers << std::endl;
// 평균 점수 계산
auto totalScore = std::accumulate(students.begin(), students.end(), 0,
[](int sum, const Student& s) { return sum + s.score; });
double averageScore = static_cast<double>(totalScore) / students.size();
std::cout << "Average score: " << averageScore << std::endl;
return 0;
}
연습 문제
- 주어진 벡터에서 짝수만 필터링하여 새로운 벡터를 반환하는 람다 함수를 작성하세요.
- 문자열 벡터를 입력받아 각 문자열의 길이를 기준으로 오름차순 정렬하는 람다 함수를 작성하세요.
참고자료
- C++ 레퍼런스의 람다 표현식 문서 : Lambda expressions
- "Effective Modern C++" by Scott Meyers의 람다 관련 항목
- CppCon 발표 : "C++ Lambda Idioms" by Scott Meyers
- "C++ Templates : The Complete Guide" by David Vandevoorde, Nicolai M. Josuttis, and Douglas Gregor (제네릭 람다와 관련된 고급 주제)