[C/C++] 람다 표현식(Lambda Expression)

할랑말랑·2026년 3월 11일

C/C++

목록 보기
19/45

1. 람다 표현식 도입( C++11 )

이름이 없는 익명 함수 객체(클로저)를 즉석에서 정의하여 사용하는 문법입니다. 주로 알고리즘, 콜백 함수 등 일회성 함수가 필요한 곳에 사용되어 코드의 간결성과 가독성을 높입니다.

기본문법

주요 기능

1. 캡처(Capture)

람다 함수 외부의 변수에 어떻게 접근할지 지정

  • [ ] : 아무 변수도 캡처하지 않음.
  • [=] : 외부의 모든 변수를 값(value)으로 캡처(복사)
  • [&] : 외부의 모든 변수를 참조(reference)로 캡처(원본에 접근)
  • [var] : 특정 변수만 값으로 캡처(복사)
  • [&var] : 특정 변수만 참조로 캡처(원본에 접근)

2. 매개변수 목록

  • 일반 함수처럼 매개변수를 정의한다.

3. 반환 타입 추론

  • 람다의 반환 타입을 명시한다. 대부분의 경우 컴파일러가 추론할 수 있어서 생략이 가능

4. {} 함수본문

  • 실제 실행될 코드를 작성

5. mutable

  • 값으로 캡처한 변수의 복사본을 수정 가능하다
  • 원본은 변하지 않는다
  • 람다 객체 내부에서 상태를 유지하 수 있게 해준다
  • 내부적으로는 operator() 에서 const를 제거
#include <iostream>

int main()
{
    int x = 20;
    int y = 10;

    // 참조로 캡처, 람다 정의
    auto simple = [&]() 
    {
        std::cout << "x: " << x << ", y: " << y << std::endl;
        x = 100;
    };

    // 람다 호출
    simple();

    // 참조한 람다 호출로 x값 변경
    std::cout << "x: " << x << std::endl;

    int a = 1, b = 2;

    auto by_value = [a, b]() { return a + b; };         // 값으로 캡처
    auto by_ref = [&a, &b]() { a++; b++; };             // 참조로 캡처
    auto all_by_value = [=]() { return a + b; };        // 모든 변수를 값으로
    auto all_by_ref = [&]() { a++; b++; };              // 모든 변수를 참조로
    auto mixed = [a, &b]() { b = a + b; };              // 혼합 캡처

    std::cout << "by_value 람다 호출 : " << by_value() << std::endl;

    int f = 10;
    // 변수 f 값으로 캡처한 변수의 복사본을 수정가능 호출할때마다 복사본의 값은 10씩 늘어난다.
    auto fxa = [f]() mutable {int a = 10; f += 10; return a + f; };
    std::cout << "fxa : " << fxa() << std::endl;
    std::cout << "fxa : " << fxa() << std::endl;
    std::cout << "f : " << f << std::endl;
}

// x: 20, y: 10
// x : 100
// by_value 람다 호출 : 3
// fxa : 30
// fxa : 40
// f : 10

2. 람다 표현식 (C++14)

  • auto 매개변수 : 매개변수 타입을 auto로 선언하여, 템플릿처럼 여러 타입에 대해 동작하는 람다를 만들 수 있다.
#include <iostream>
#include <string>

int main()
{
    // 일반화된 람다 : 매개변수 타입이 auto
    auto print = [](auto value) 
    {
        std::cout << value << std::endl;
    };

    print(42);                     // int
    print(3.14);                   // double
    print("Hello");                // const char*
    print(std::string("World"));   // std::string

    // 여러 auto 매개변수 사용
    auto add = [](auto a, auto b) 
    {
        return a + b;
    };

    std::cout << add(10, 20) << std::endl;                      // 30 (int)
    std::cout << add(1.5, 2.5) << std::endl;                    // 4.0 (double)
    std::cout << add(std::string("Hello "), std::string("World")) << std::endl; // "Hello World"
}
  • 초기화 캡처 : 캡처 절에서 새로운 변수를 만들고 초기화할 수 있게 되었다. 이를 통해 이동 전용 타입을 람다로 캡처할 수 있다.
#include <iostream>
#include <memory>
#include <vector>

int main()
{
    // 1. 초기화 캡처로 새 변수 생성
    int x = 10;
    auto lambda1 = [value = x * 2]() {
        std::cout << "value: " << value << std::endl; // 20
    };
    lambda1();

    // 2. 이동 캡처 (move-only 타입인 unique_ptr 캡처)
    auto ptr = std::make_unique<int>(100);

    // C++11에서는 unique_ptr을 람다로 캡처할 방법이 없었음!
    // C++14부터는 이동 캡처가 가능:
    auto lambda2 = [ptr = std::move(ptr)]() {
        std::cout << "ptr value: " << *ptr << std::endl;
    };
    lambda2();
    lambda2();
    // ptr은 이제 nullptr (소유권이 람다로 이동됨)

    // 3. 벡터를 이동 캡처
    std::vector<int> vec = { 1, 2, 3, 4, 5 };

    auto lambda3 = [vec = std::move(vec)]() {
        for (int n : vec) 
        {
            std::cout << n << " ";
        }
        std::cout << std::endl;
    };
    lambda3();
    lambda3();
    // vec은 이제 비어있음 (이동됨)
}
  • std::move함수로 rvalue참조로 캐스팅이 되서 객체의 복사(Copy) 대신 자원의 소유권(Ownership)을 이전(Move)한다.

  • 반환 타입 추론 개선 : 더 복잡한 경우에도 반환 타입을 자동으로 추론할 수 있게 됨

3. 람다 표현식 (C++17)

constexpr 람다

constexpr은 constant expression(상수 표현식)의 약자로, 컴파일 타임에 값을 계산할 수 있게하여 더 최적화된 코드를 생성하도록 돕는다.
[C/C++] constexpr 참고

#include <iostream>

int main()
{
    // C++17: 람다 표현식 constexpr 명시적, 조건 충족 시 자동으로 constexpr
    // 조건 불충족 - 명시해도 소용없음
    constexpr auto square = [](int x) {
        return x * x;
    };

    constexpr int result = square(5); // 컴파일 타임에 25 계산

    std::cout << result << std::endl; // 25

    // 배열 크기로 사용 가능
    int arr[square(3)]; // int arr[9];
}
  • *this 캡처 (복사 캡처)

#include <iostream>

class Counter
{
public:
    int count = 0;

    auto getIncrementer()
    {
        // [*this]: 객체 전체를 복사
        return [*this]() mutable {
            count++; // 복사본의 count를 수정
            std::cout << "[*this] 복사본 count: " << count << std::endl;
        };
    }

    auto getIncrementer2()
    {
        // [this]: 객체 참조
        return [this]() {
            count++;
            std::cout << "[this] 참조 count: " << count << std::endl;
        };
    }
};

int main()
{
    Counter c;

    auto inc = c.getIncrementer();
    auto inc2 = c.getIncrementer2();

    inc();
    inc();

    std::cout << "원본 count : " << c.count << std::endl;
    inc2();
    inc2();

    std::cout << "원본 count : " << c.count << std::endl;
}
  • [this]는 객체를 참조하여 수정 하면 원본이 수정된다.
  • [*this]는 객체를 복사하고 mutable를 사용하여 복사한 캡처를 수정하여 사용할수있지만 원본은 건드리지 않는다.

0개의 댓글