관리 메뉴

Bull

[C++/syntax] Lambda (람다식) 본문

Computer Language/C++

[C++/syntax] Lambda (람다식)

Bull_ 2024. 3. 15. 18:27

개념

C++에서 람다 식은 익명 함수를 생성하기 위한 간결한 방법을 제공한다.

 

C++11부터 도입된 람다는 런타임에 정의되며, 주로 알고리즘에서 콜백 함수를 제공하거나,

 

스코프 내의 변수를 캡처하여 사용하는 용도로 활용한다.

 

람다 식의 기본 구조

바로 호출하는 경우

[캡처](매개변수 목록) {
    // 함수 본문
}(인자들);

 

초기화하는 경우

변수 = [캡처](매개변수 목록) -> 반환 타입 {
    // 함수 본문
    // return ~;
}(인자들);

[](){}()로 외우면 편하다.

 

캡처 목록

람다가 외부 스코프의 변수를 사용할 때, 그 변수들을 어떻게 캡처할 것인지를 정의한다.

 

값으로 캡처할지([=]), 참조로 캡처할지([&]), 혹은 특정 변수만 선택적으로 캡처할지([x, &y]) 결정할 수 있다.


매개변수 목록

함수와 같이, 람다 식도 매개변수를 가질 수 있다.

 

매개변수가 필요 없는 경우 이 부분을 비워 둘 수 있습니다.


반환 타입

C++14부터는 반환 타입을 명시적으로 적지 않아도 컴파일러가 반환 타입을 추론할 수 있다.

 

그러나 복잡한 람다 식에서는 명시적으로 반환 타입을 제공하는 것이 좋다.

 

인자들

람다 함수에 전달될 인자를 나타낸다.

 

이 방식은 람다 식이 정의된 직후에 한 번만 실행되어야 할 때 유용하다.

 

반환 타입을 명시하는 부분은 여전히 람다 식의 시작 부분에서 정의되어야 하며, 람다 식을 호출하는 괄호는 람다 본문 뒤에 위치한다.

 

 

예시

캡처X, 반환X

auto printMessage = []() {
    std::cout << "Hello, World!" << std::endl;
};
printMessage(); // 출력: Hello, World!

캡처O, 반환X

std::string message = "Hello, C++!";
auto printCustomMessage = [message]() {
    std::cout << message << std::endl;
};
printCustomMessage(); // 출력: Hello, C++!

캡처X, 반환O

auto add = [](int x, int y) -> int {
    return x + y;
};
std::cout << "The sum is: " << add(5, 3) << std::endl; // 출력: The sum is: 8

캡처O, 반환O

int factor = 2;
auto multiply = [factor](int x) -> int {
    return x * factor;
};
std::cout << "The result is: " << multiply(5) << std::endl; // 출력: The result is: 10

바로 호출(인자X)

[]() {
    std::cout << "Hello, World!" << std::endl;
}();

바로 호출(인자O)

[](int x, int y) {
    std::cout << "Sum: " << x + y << std::endl;
}(10, 5);

응용

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

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

    // 모든 요소에 대해 multiplier를 곱하고 결과를 출력
    std::for_each(v.begin(), v.end(), [multiplier](int &n) {
        n *= multiplier;
        std::cout << n << " ";
    });
    
    return 0;
}
>>> 2 4 6 8 10

 

 

 

캡처를 사용하는 이유?

같은 블록에 있는 변수인데 왜 캡처에 변수를 따로 넣어주는건지?

 

캡처에 안넣어도 사용할 수 있다고 생각했는데...

 

C++의 람다 식 설계와 스코프 관리 규칙에 의하면,

 

람다 식은 본질적으로 익명의 함수 객체이며, 자신이 정의된 스코프 외부의 변수에 접근할 때 특별한 규칙을 따라야 한다.

 

(즉, 외부 스코프 변수에 바로 접근이 불가하다는 것. 실제로 캡처에 안넣고 하면 에러난다.)

 

1. 안전한 접근 보장

외부 스코프의 변수를 람다 내에서 사용할 때, 그 변수가 람다가 실행되는 시점에도 유효한 상태임을 보장하기 위함이다.

 

값으로 캡처하면 해당 시점의 변수 값을 복사하여 람다 함수 내에서 안전하게 사용할 수 있다.

 

참조로 캡처하면, 람다 실행 시 변수가 유효한 스코프에 있음을 개발자가 보장해야 한다.

2. 명시성과 의도의 명확화

캡처 목록을 사용함으로써 람다가 어떤 외부 변수에 의존하고 있는지 명확하게 표현된다.

 

이는 코드의 의도를 명확히 하고, 유지보수성을 향상시키는 데 도움이 된다.

3. 스코프와 생명주기 관리

람다 식이 실행될 때 외부 변수가 더 이상 유효하지 않을 수도 있는 상황(예: 변수의 스코프를 벗어나는 경우)을 관리한다.

 

변수를 캡처함으로써 람다 식이 해당 변수의 복사본 또는 참조를 안전하게 보관할 수 있게 한다.

4. 유연성 제공

캡처 목록을 통해 개발자는 람다가 접근해야 하는 외부 변수를 유연하게 선택할 수 있다.

 

모든 외부 변수를 자동으로 캡처하는 것([=] 또는 [&])과 달리, 특정 변수만 선택적으로 캡처할 수도 있다([x, &y]).

 

이는 불필요한 캡처를 피하고, 메모리 사용과 성능에 영향을 줄 수 있는 요소를 최적화하는 데 도움이 된다.