ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • _C++_lambda
    Programming/_C++ 2023. 9. 20. 18:02

    람다(lambda)는 익명의 함수를 정의하는 표현식을 의미한다. 람다를 통해 클로져(Closure) 클래스가 정의됨.

    람다는 캡처(capture), 인자(parameter), 반환형(return type), 몸통(body)로 구성되었다.

    //case 1
    auto f = [captures](parameters) -> return type {
    	body
        /*
        captures: 캡처들이 들어감
        parameters: 함수의 인자들이 들어감
        return type: 함수의 반환형
        body: 함수의 몸통    
        */
    }
    
    //case 2
    #include <functional>
    function<void(int)> g = [](int param) {
    	//function 템플릿을 사용하여 형식 지정이 가능하다.
    };

    위의 두 구조중 1번 구조가 효과적이다.

    내부를 알 수 없는 경우 auto로 컴파일러가 결정할 수 있게 하는 것이 효과적이다.

    우리가 형식을 지정한다면, 메모리가 부족할 수 있어서 효과적이지 않다.

     

    람다는 위와 같은 구조를 가지며, return type의 명시는 생략가능하다. 생략한다면, void 타입으로 추론한다.

    만약, return문이 여러번 등장하는데 return type이 일치하지 않으면 return type을 생략할 수 없다.

     

    1. capture

     다른 프로그래밍 언어에서는 capture를 정해주지만, C++은 사용자가 정해야 한다. (이는 장점이자 단점?)

    capture문은 함수 몸체에서 외부 변수에 접근하기 위해 사용된다.

    capture할 이름 앞에 '&'를 붙이면 참조로 capture하고, 그렇지 않으면 값으로 capture한다.

    값으로 capture를 한다면, const로 취급된다.

    #include <iostream>
    using namespace std;
    
    int main()
    {
        int x = 0, y = 1;
        auto f = [&x, y](){ //&x는 참조, y는 값으로 캡쳐
            return x += y;
        };
        return 0;
    }

    capture는 댕글링 포인트 버그를 발생할 가능성이 있으므로 유의하자!!

    // 간단한 예시
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    int main()
    {
        []() { std::cout << "Hello lambda!" << std::endl; }();
    
        int sum = 0;
        vector<int> nums {1, 2, 3};
        std::for_each(nums.begin(), nums.end(), [&sum](int& number) { sum += number; });
        cout << sum;
        return 0;
    }

     

    기본 캡쳐 모드

    기본 캡쳐 모드는 스코프 내의 모든 이름을 일관적으로 capture하는 것이다.

    auto f = [&](){ //기본 캡쳐 모드는 참조
    };
    
    auto g = [=](){ //기본 캡쳐 모드는 값
    };
    
    auto h = [&, y](){//기본 캡쳐 모드는 참조이고 y는 값으로 캡쳐
    };

    위와 같이 기본 capture 모드를 정하고 추가되는 capture는 다른 방법으로 할 수 있다.

    기본 capture 모드는 캡쳐할 대상을 나열하지 않기 때문에, capture 내용을 제대로 파악하지 않을 수 있으므로 외부의 변화와 무관하게 자기 완결적으로 오해하기 쉽다고 한다.

    또한 전역 변수, 같은 블록 스코프 내의 static 변수들은 캡쳐가 불가능하다고 한다.(할 필요도 없음)

    우리가 기본 값 캡쳐모드를 사용하면서 이러한 전역 변수, static 변수들이 값으로 복사되었다고 생각할 수 있다.

     

    초기화 캡쳐(학습중)

    이동 전용 객체의 경우 복사가 삭제되어 값 캡쳐가 불가능 하다. 참조 캡쳐는 가능하지만, 참조 대상 객체가 먼저 소멸 하는 경우가 있다고 한다.

    초기화 캡쳐는 C++14에서 추가되었으며, 위와 같은 경우에서 사용된다고 한다.

     

    만약, 클로져 자체가 재귀 함수라면, function으로 선언해야 한다고 한다.(auto로 선언하는 것이 불가능)

    #include <functional>
    
    using namespace std;
    
    int main()	[
        auto f = [&f](){ // 컴파일 에러 발생
        	f();
        };
        
        function<void()> g = [&g](){
        	g();
        };
    	return 0;
    }

    auto로 선언을 하게 되면 컴파일 시점에서 f의 형식이 불완전하게 되어 capture가 불가능 하다고 한다.

    'Programming > _C++' 카테고리의 다른 글

    _C++_Template  (1) 2023.09.24
    _C++_컨테이너 어댑터와 비트셋 컨테이너  (0) 2023.09.20
    _C++_Scope  (0) 2023.09.20
    _C++_알고리즘_Part_1  (0) 2023.09.19
    _C++_Smart_Pointer  (1) 2023.09.19
Designed by Tistory.