안녕하세요. 이번 글에서는 C++11부터 사용할 수 있는 람다 표현식(lambda expression)에 대해 알아보겠습니다.
std::sort, std::find_if 같은 알고리즘에 비교 기준이나 조건을 넘길 때, 예전에는 별도의 함수나 함수 객체(functor)를 정의해야 했습니다. 람다를 사용하면 호출하는 자리에서 이름 없는 함수를 바로 만들 수 있어 코드가 짧아지고, 로직이 사용되는 위치와 가까워집니다.
기본 문법
[캡처](매개변수) -> 반환형 { 본문 }
- [ ] : 캡처 절. 바깥 변수를 람다 안에서 사용하는 방식을 지정합니다.
- ( ) : 매개변수 목록. 인자가 없으면 생략할 수 있습니다.
- -> 반환형 : 생략하면 return 문으로 추론합니다.
- { } : 함수 본문입니다.
알고리즘과 함께 사용하기
헤더는 <algorithm>, <vector>를 사용합니다. 내림차순 정렬 기준을 람다로 전달하는 예제입니다.
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 5, 2, 8, 1, 9 };
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
for (int n : v)
std::cout << n << ' ';
std::cout << '\n';
}
실행 결과는 9 8 5 2 1 입니다.
캡처: 값 복사와 참조
캡처 절에 변수 이름을 적으면 값 복사, 앞에 &를 붙이면 참조로 가져옵니다. [=]는 사용하는 변수를 모두 복사, [&]는 모두 참조로 가져옵니다.
#include <iostream>
int main()
{
int base = 10;
auto addByValue = [base](int x) { return x + base; };
auto addByRef = [&base](int x) { return x + base; };
base = 100;
std::cout << addByValue(1) << '\n';
std::cout << addByRef(1) << '\n';
}
값 캡처는 람다를 만든 시점의 값(10)을 보관하므로 11, 참조 캡처는 호출 시점의 현재 값(100)을 읽으므로 101이 출력됩니다. 참조 캡처한 변수가 람다보다 먼저 소멸하면 댕글링 참조가 되므로, 람다를 함수 밖으로 반환하거나 다른 스레드로 넘길 때는 값 캡처를 사용합니다.
mutable
값으로 캡처한 변수는 기본적으로 const여서 람다 안에서 수정할 수 없습니다. mutable을 붙이면 람다 내부의 복사본을 수정할 수 있으며, 원본에는 영향을 주지 않습니다.
#include <iostream>
int main()
{
int count = 0;
auto counter = [count]() mutable { return ++count; };
std::cout << counter() << '\n';
std::cout << counter() << '\n';
std::cout << count << '\n';
}
출력은 1, 2, 0 순서입니다. 람다 안의 복사본은 호출할 때마다 유지되며 증가하지만, 바깥의 count는 그대로 0입니다.
제네릭 람다 (C++14)
C++14부터는 매개변수 타입에 auto를 쓸 수 있습니다. 하나의 람다로 여러 타입을 처리할 수 있습니다.
#include <iostream>
#include <string>
int main()
{
auto add = [](auto a, auto b) { return a + b; };
std::cout << add(1, 2) << '\n';
std::cout << add(1.5, 2) << '\n';
std::cout << add(std::string("ab"), std::string("cd")) << '\n';
}
출력은 3, 3.5, abcd 입니다.
정리
- 람다는 [캡처](매개변수) { 본문 } 형태로 이름 없는 함수를 만듭니다.
- 값 캡처는 복사본을, 참조 캡처는 원본을 사용하며 수명 관리에 주의해야 합니다.
- 값 캡처한 변수를 람다 안에서 수정하려면 mutable이 필요합니다.
- C++14의 auto 매개변수로 제네릭 람다를 만들 수 있습니다.
이상으로 람다 표현식에 대해 간단히 알아보았습니다.