본문 바로가기

카테고리 없음

람다(lambda) in C++

C++에서 **람다(lambda)**는 **익명 함수(anonymous function)**를 정의하는 기능으로, 간결한 코드 작성과 함수 객체를 쉽게 만들 수 있도록 도와줍니다. C++11부터 도입되었으며, C++14, C++17, C++20을 거치며 더 강력하게 발전했습니다.


람다 기본 문법

[캡처](매개변수) -> 반환형 { 함수 본문 };
  • [캡처] : 외부 변수 캡처 방식 지정 (&, =, 특정 변수 선택 가능)
  • (매개변수) : 함수의 매개변수
  • -> 반환형 : 반환형 (생략 가능, 자동 추론됨)
  • { 함수 본문 } : 실행할 코드

람다 기본 사용 예제

#include <iostream>

int main() {
    // 기본적인 람다 표현식
    auto add = [](int a, int b) { return a + b; };

    std::cout << "3 + 5 = " << add(3, 5) << std::endl; // 출력: 3 + 5 = 8
    return 0;
}

캡처 방식

람다는 외부 변수를 캡처하여 사용할 수 있습니다.

1. 값 캡처 (=)

#include <iostream>

int main() {
    int x = 10;
    auto lambda = [=]() { std::cout << "x = " << x << std::endl; };
    lambda(); // 출력: x = 10
}

2. 참조 캡처 (&)

#include <iostream>

int main() {
    int x = 10;
    auto lambda = [&]() { x += 5; };
    lambda();
    std::cout << "x = " << x << std::endl; // 출력: x = 15
}

3. 특정 변수만 캡처

#include <iostream>

int main() {
    int a = 10, b = 20;
    
    auto lambda1 = [a]() { std::cout << "a = " << a << std::endl; }; // a만 값 캡처
    auto lambda2 = [&b]() { b += 5; }; // b만 참조 캡처
    
    lambda1(); // 출력: a = 10
    lambda2();
    std::cout << "b = " << b << std::endl; // 출력: b = 25
}

4. 반환형 지정

#include <iostream>

int main() {
    auto divide = [](double a, double b) -> double {
        if (b == 0) return 0.0;
        return a / b;
    };

    std::cout << "10 / 2 = " << divide(10, 2) << std::endl; // 출력: 10 / 2 = 5
}

5. mutable 키워드

람다에서 [=] 또는 [변수]를 사용하여 값 캡처를 하면, 해당 변수는 읽기 전용이 됩니다. 즉, 람다 내부에서 값을 변경할 수 없습니다. 하지만 mutable 키워드를 사용하면 값 캡처된 변수를 변경할 수 있습니다. 단, 이는 원래 변수에는 영향을 미치지 않습니다.

#include <iostream>

int main() {
    int x = 10;
    
    auto lambda = [x]() mutable {
        x += 5;
        std::cout << "x in lambda = " << x << std::endl;
    };

    lambda();
    std::cout << "x outside lambda = " << x << std::endl;
    return 0;
}

출력:

x in lambda = 15
x outside lambda = 10
  • mutable을 사용하면 캡처된 변수의 복사본을 변경할 수 있지만, 원래 변수에는 영향을 주지 않습니다.

6. 람다를 함수 인자로 전달

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    std::for_each(numbers.begin(), numbers.end(), [](int n) {
        std::cout << n * 2 << " ";
    });
}

7. 람다와 std::function

#include <iostream>
#include <functional>

int main() {
    std::function<int(int, int)> add = [](int a, int b) { return a + b; };
    std::cout << "10 + 20 = " << add(10, 20) << std::endl;
}

8. 람다 사용 시 주의할 점

캡처한 변수의 생존 기간

#include <iostream>
#include <functional>

std::function<void()> getLambda() {
    int x = 10;
    return [&]() { std::cout << x << std::endl; }; // 위험!
}

int main() {
    auto lambda = getLambda();
    lambda(); // x는 이미 소멸되어 undefined behavior 발생 가능!
}

 

mutable 없이 값 캡처 변수 수정 불가

int x = 10;
auto lambda = [x]() { x += 5; }; // 오류 발생 (x는 읽기 전용)

 

람다의 크기 문제

  • 람다는 내부적으로 함수 객체 (클래스)로 변환되므로, 캡처된 변수만큼 메모리를 차지합니다.
  • 불필요한 캡처는 피하는 것이 좋습니다.

Q&A

Q1: 람다에서 mutable을 언제 사용해야 하나요?

A: 값 캡처된 변수를 람다 내부에서 수정해야 할 때 사용합니다. 단, 원래 변수에는 영향을 주지 않습니다.

Q2: 람다에서 참조 캡처를 사용할 때 주의해야 할 점은?

A: 참조 캡처한 변수는 원래 변수의 수명에 의존하므로, 함수가 끝난 후 접근하면 dangling reference 오류가 발생할 수 있습니다.

Q3: 람다는 어느 정도의 오버헤드가 발생하나요?

A: 람다는 내부적으로 함수 객체로 변환되며, 캡처하는 변수에 따라 크기가 증가할 수 있습니다. 따라서 불필요한 캡처는 피하는 것이 좋습니다.


결론

  • C++ 람다는 코드의 간결성과 가독성을 높이는 강력한 도구입니다.
  • 하지만 캡처 방식과 변수의 생명 주기를 신경 써야 합니다.
  • 특히, 참조 캡처는 dangling reference 문제를 유발할 수 있으므로 주의해야 합니다! 🚀