-
_C++_lambdaProgramming/_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