Fundamental of CS/: : C++

[Functional Programming] Lambda Expression

Jay.P Morgan 2024. 7. 31. 20:10

 

 

람다 익스프레션(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)의 종류

람다에서 가장 중요한 부분은 캡처입니다.

캡처는 외부 변수나 객체를 람다 내부에서 사용할 수 있도록 가져오는 기능입니다.

대표적으로:

  1. 값 캡처(Capture by Value)
  2. 참조 캡처(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++ 문법이라는 점입니다.

또한 람다의 효율성과 동작 방식을 이해하려면 캡처 방식(값/참조)  메모리 구조를 이해하는 것이 중요합니다.