본문 바로가기
공부

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

by MY블로그 2023. 5. 27.

람다 함수에 대하여 정리 합니다.

정확히는 람다 표현식(Lambda Expression) 이라고하며 람다 함수(Lambda Function) 라고도 합니다.


람다 표현식 이란?

람다 표현식은 함수나 함수 객체를 별도로 정의 하지 않은 상태에서 필요한 지점에서 임시적인(익명스코프처럼) 함수를 생성하여 사용 할 수 있는 함수 입니다. 클로저(Closure[폐쇄,종결])를 의미하기도 합니다.

람다함수는 함수 포인터에 담을 수 있기때문에 즉흥적으로 사용하기 편리합니다.

단, 이름이 없는 임시적인 것이기때문에 재차 사용하거나 검색하기 어려워 사용을 권장하지는 않는다고 합니다.


람다 표현식의 형태

[①capture] (②parameters) <③specifiers> - > <④return_type> {⑤body}

의 형태 식으로 구성이 됩니다.


①capture 캡처

제일 앞의 대괄호 [ ] 를 캡처 블록 이라고 합니다.

캡처의 종류
[  ] 빈 대괄호는 캡처를 하지 않습니다.
[=] 값에 의한 캡처이며 모든 외부 변수를 캡처하고 람다 식 내부에서 값 수정이 불가 합니다.
[&] 참조에 의한 캡처이며 모든 외부 변수를 캡처합니다. 식 내부에서 값 수정이 가능 합니다.
[변수이름] 특정 변수 값으로 캡처이며 람다 식 내부에서 값 수정이 불가 합니다.
[&변수이름] 특정 변수를 참조하여 캡처 합니다. 식 내부에서 값 수정이 가능 합니다.
[캡처리스트] 복수의 캡처를 섞어 사용이 가능합니다. 식 내부의 값 수정은 캡처 종류에 따릅니다.

[  ] 사용 법

//방법1 
auto noCapture = []() {std::cout<< "No Capture"<<std::endl;};
noCapture();

//방법2 바로 실행
[]() {std::cout<< "No Capture2"<<std::endl;};

[=] 사용 법 ( 값 수정 불가 )

int a = 10;
int b = 50;

auto max = [=]() { return a > b ? a : b; }; // 삼항 연산자

cout <<"Max is " << max() <<endl; // 출력 Max is 50

[&] 사용 법 ( 값 수정 가능 )

int a = 10;
int b = 50;

auto changeValue = [&]()
{
    a = 20;
    b = 12;
};

cout<<a<<" "<<b<<endl; // 출력 결과 10 50 변수 생성 및 초기화 한 값 그대로

changeValue(); // 함수호출시 참조 람다로 내부에서 값 변경 실행

cout <<"result : " << a << " " << b << endl; // 출력 결과 20 12 변경된 값

[변수이름] 사용 법 ( 값 수정 불가 )

int a = 10;
int b = 20;
int c = 30;

auto printValue = [a, b, c]()
{
    //a = 20; error !! 값 변경 불가능
    //b = 40; error !! 값 변경 불가능
    //c = 60; error !! 값 변경 불가능
    cout << a << "/" << b << "/" << c << endl;
};

printValue(); // 출력결과 10 / 20 / 30

 

[&변수이름] 사용 법 ( 값 수정 가능 )

int a = 10;
int b = 50;
int c = 20;

auto changeValue = [&a, &b, &c]()
{
    a = 20;
    b = 40;
    c = 60;
    cout << a << "/" << b << "/" << c << endl; // 20 / 40 / 60 으로 변경가능! 
};

changeValue();// 출력 결과 20 / 40 / 60

[캡처 리스트] 사용 법 ( 조건에따라 값 수정이 다름 / [=][변수이름] 불가 / [&][&변수이름] 가능 )

int a = 10;
int b = 50;
int c = 20;

auto changeValue = [=, &b]()
{
    //a = 20;	// 값에 의한 캡처로 수정은 불가능 합니다.
    b = 40;		// 참조에 의한 캡처로 수정이 가능 합니다.
    //c = 60;	// 변수에 의한 캡처로 수정이 불가능 합니다.
    cout << a << "/" << b << "/" << c << endl;
};

changeValue(); // 출력 결과 10 / 40 / 20

②parameters 파라미터

파라미터 부분은 선택 사항 입니다.

auto noCapture = [] {std::cout << "No Capture" << std::endl; }; //빈 괄호를 생략 가능
noCapture(); // 출력 No Capture

auto noCapture2 = []() {std::cout << "No Capture2!" << std::endl; }; // 빈 괄호 포함
noCapture2(); // 출력 No Capture2!

// 둘다 작동 합니다.

int scores1 = 6;
int scores2 = 23;

auto add = [] (int a, int b) // 파라미터(매개변수) 포함
{
	return a + b;
};

cout<< add(scores1, scores2) <<endl; // 출력 29

③specifiers 지시자(지정자)

컴파일러는 람다 표현식을 이름이 없는 함수객체(펑터)로 변환 합니다.

캡처한 변수는 이 함수객체의 데이터 멤버가 되고  값으로 캡처한 변수는 함수객체의 데이터 멤버로 복제 됩니다.

 

함수객체마다 함수 호출 연산자인 operator()가 구현이 되어 있습니다.

람다 표현식의 경우 이 연산자는 기본적으로 const(상수)로 설정 합니다.

때문에 non_const(비상수) 변수를 람다 표현식에 값으로 캡처하더라도 람다 표현식 안에서 복제한 이 값을 수정 할 수 없습니다.

 

만일 람다 표현식에서 변수를 수정하고자 할 경우 mutable 을 사용 하여야 합니다.

int a = 10;


auto changeValue() = [a]() 
{
   cout<<++a<<endl; //컴파일 에러
};

cout << changeValue(); // 출력 불가


auto changeValue2() = [a]() mutable // mutable 선언
{
   cout<<++a<<endl;
};

cout << changeValue2(); // 변수 a에 전위증가로인하여 11 출력

④return_type 반환형

반환 형을 적지 않는다면 컴파일러가 자동적으로 추론 합니다.

function 을 사용하여 함수가 람다 표현식을 리턴하도록 만들 수 있습니다.

function<int(void)> multiplyBy2Lambda(int x)
{
	return [x]{ return 2 * x};
}

// auto 사용 가능
auto fn =  multiplyBy2Lambda(5);
cout<< fn() <<endl; // 출력결과 매개변수 5와 반환 *2로인하여 10 출력

⑤body 함수내부

함수내부의 범위는 함수가 실행할 동작을 결정하는 구간 입니다.

람다 표현식에서 기본적인 틀만 놓고 모두 생략한 후 body 부분에 실행할 동작을 넣어 즉흥 적으로 필요한 기능을 구현 할 수 있습니다.

auto onlybody = []() { cout << "only body" << endl; };

onlybody(); // only body 출력

 


람다 표현식의 장단점

장점

위의 body 의 예시처럼 아주 간략한 코드로 함수를 즉흥적으로 만들어 실행 시킬 수 있습니다.

 

단점

콜스택을 보기 힘들기 때문에 디버깅을 하기 어려워 질 수 있습니다.

함수는 재사용 성의 특성을 가지고 있습니다. 하지만 람다 표현식은 재사용이 불가능 합니다.

람다 표현식은 짧고 즉흥적인만큼 찾기도 어렵습니다.

코드의 중복 발생 확률이 높습니다.

댓글