람다 익스프레션(Lambda Expression)
1. 람다 익스프레션과 함수 객체의 관계
이전 시간에는 함수 객체(Function Object) 를 이용해 C++에서 클로저와 비슷한 기능을 구현하는 방법을 알아보았습니다.
함수 객체는 클래스 내부에 멤버 변수를 저장하고 operator()를 오버로딩하여 함수처럼 호출할 수 있도록 만든 객체입니다.
예를 들어:
- Plus(3) 객체 → 내부에 3이라는 값을 저장
- Plus(5) 객체 → 내부에 5라는 값을 저장
이후 함수처럼 호출하면 저장된 값을 이용해 연산을 수행합니다.
즉:
- 함수처럼 동작하지만 실제로는 객체
- 내부 상태(State)를 저장 가능
- 함수형 프로그래밍의 핵심인 "함수를 객체처럼 다룬다"를 구현
2. 람다 익스프레션 등장
C++11부터 람다 익스프레션이 추가되었습니다.
람다는 함수 객체를 더 간결하게 작성할 수 있도록 만든 문법입니다.
기존 함수 객체:
class Plus
{
int value;
public:
Plus(int v) : value(v) {}
int operator()(int x)
{
return value + x;
}
};
람다 표현:
auto plus3 = [value = 3](int x)
{
return value + x;
};
두 코드는 결과적으로 동일하게 동작합니다.
즉:
- 함수 객체 → 직접 클래스를 작성
- 람다 → 컴파일러가 내부적으로 함수 객체 클래스를 생성
3. 람다와 함수 객체의 내부 동작
람다:
auto plus3 = [value = 3](int x)
{
return value + x;
};
컴파일러 관점에서는 대략 다음과 같은 클래스를 만든다.
class LambdaObject
{
int value;
public:
LambdaObject(int v)
: value(v)
{}
int operator()(int x)
{
return value + x;
}
};
따라서:
- 람다 객체도 메모리에 생성됨
- 내부 변수는 멤버 변수처럼 저장됨
- 호출하면 operator()가 실행됨
어셈블리 레벨에서도 함수 객체와 람다는 거의 동일하게 동작합니다.
4. 람다 기본 문법
일반적인 람다 문법:
[캡처](매개변수) -> 반환타입
{
함수 내용
};
예:
auto lambda = [](int x)
{
return x * 2;
};
구성 요소:
① 캡처(Capture)
외부 변수를 람다 내부로 가져오는 부분
[value]
② 매개변수
일반 함수와 동일
(int x)
③ 반환 타입
필요하면 지정
-> int
대부분은 컴파일러가 추론하기 때문에 생략한다.
④ 함수 바디
일반 함수와 동일
5. 캡처(Capture)의 종류
람다에서 가장 중요한 부분은 캡처입니다.
캡처는 외부 변수나 객체를 람다 내부에서 사용할 수 있도록 가져오는 기능입니다.
대표적으로:
- 값 캡처(Capture by Value)
- 참조 캡처(Capture by Reference)
6. 값 캡처 (Capture by Value)
예:
int value = 3;
auto lambda = [value](int x)
{
return value + x;
};
동작:
- value가 복사됨
- 람다 객체 내부에 새로운 멤버 변수 생성
메모리 구조:
Lambda Object
+-------------+
| value = 3 |
+-------------+
특징:
- 원본 변수와 독립적
- 안전함
- 큰 객체를 복사하면 비효율적
즉:
작은 데이터 → 값 캡처 적합
7. 참조 캡처 (Capture by Reference)
예:
int value = 3;
auto lambda = [&value](int x)
{
return value + x;
};
동작:
- 복사하지 않음
- 기존 변수의 참조를 저장
메모리 구조:
Lambda Object
+----------------+
| reference ---- |
+----------------+
|
v
value = 3
장점:
- 복사가 발생하지 않음
- 큰 객체에 효율적
단점:
- 원본 객체의 수명이 중요함
예:
auto lambda = [&value](){};
value가 삭제된 이후 lambda 호출
이 경우:
- 참조 대상이 사라짐
- 잘못된 메모리 접근 가능
8. 자동 캡처
람다는 캡처를 자동으로 지정할 수도 있습니다.
값 자동 캡처
[=]
의미:
- 사용하는 외부 변수를 모두 값으로 캡처
예:
int a = 10;
auto lambda = [=]()
{
cout << a;
};
컴파일러가 자동으로 a를 복사한다.
참조 자동 캡처
[&]
의미:
- 사용하는 외부 변수를 모두 참조로 캡처
예:
int a = 10;
auto lambda = [&]()
{
a++;
};
람다가 원본 값을 직접 수정한다.
9. 명시적 캡처와 자동 캡처 사용
자동 캡처는 편하지만 일반적으로는 필요한 변수만 직접 지정하는 방식이 많이 사용된다.
예:
[x, y]
이 방식의 장점:
- 어떤 변수를 사용하는지 명확함
- 불필요한 캡처 방지
- 코드 이해가 쉬움
10. 핵심 정리
함수 객체
- 클래스로 만든 호출 가능한 객체
- 상태 저장 가능
- 함수처럼 사용 가능
람다 익스프레션
- 함수 객체를 쉽게 작성하는 문법
- 내부적으로는 컴파일러가 함수 객체 클래스를 생성
- STL 알고리즘과 함께 매우 많이 사용됨
캡처
람다 내부에서 외부 데이터를 사용하는 방법
| 방 식 | 의 미 | 특 징 |
| [x] | 값 캡처 | 복사, 안전 |
| [&x] | 참조 캡처 | 복사 없음, 효율적 |
| [=] | 전체 값 캡처 | 자동 복사 |
| [&] | 전체 참조 캡처 | 자동 참조 |
이번 내용의 핵심은 람다는 새로운 함수 형태가 아니라, 기존 함수 객체를 더 편리하게 표현하는 C++ 문법이라는 점입니다.
또한 람다의 효율성과 동작 방식을 이해하려면 캡처 방식(값/참조) 과 메모리 구조를 이해하는 것이 중요합니다.
'Fundamental of CS > : : C++' 카테고리의 다른 글
| [Functional Programming] Higher Order Functions (0) | 2024.07.31 |
|---|---|
| [Functional Programming] Lambda This (0) | 2024.07.31 |
| [Functional Programming] Functional Programming, Function Object Intro (0) | 2024.07.31 |
| [Functional Programming] C++ Lambda Function (0) | 2024.07.31 |
| [Templates] C++20 Template Concept (0) | 2024.07.16 |