빼미의 개발일기

[C++] - 람다식 본문

프로그래밍/C & C++

[C++] - 람다식

빼미01 2023. 5. 9. 14:58
람다식?

- 람다식, 람다함수 라고 불린다. 함수나 함수 객체를 별도로 정의하지 않고, 필요한 지점에서 바로 함수를 직접 만들어 쓸수 있는 익명함수이다. 실무적으로도 코드의 간결화와 지연 연산 성능을 높이고, 반복 관련 코드의 불필요한 부분들을 제거 할 수 있다는 점에서 중요한 개념이다.

 

◆ 장점 :

  • 코드의 간결화 - 반복적으로 사용되지 않는 함수를 간결하게 작성할 수 있다.
  • 퍼포먼스 향상 - 지연 연산을 지원하는 방식(스트리밍)을 통해 메모리상의 효율성 및 불필요한 연산을 배제 할 수 있다.

◆ 단점 :

  • 모든 원소를 전부 순회하는 경우의 코드면 람다식이 조금 느릴 수 있다.
  • 함수 외부의 캡처를 위한 캡처를 하는 시간제약, 논리제약적인 요소도 고려해야 하며, 디버깅 시 함수 콜스텍 추적이 어렵다.
  • 람다식 남용시 코드를 이해하기 어려워 질 수 있다.(람다식은 적절한 경우에만 사용하는 것이 좋음)

람다식 구조
[captures](parameters) specifiers ->return_type {body}
  • captures(캡처) : 
    번역하면 갈무리 모드라고도 하고,  대괄호안에 어떤 변수를 지정하면 람다 표현식의 본문에서 그 변수를 사용하게 만들 수 있다.
int three = 3;

auto Lambda1 = [](int x){ return x + 3; }			
// 비어있음 : 캡처를 사용하지 않음.

auto Lambda2 = [three](int x){ return x + three; }; 
// 변수이름 : 특정 변수 값을 사용(수정 불가)

auto Lambda2_1 = [three = 2](int x){ return x + three; };	 
// 변수이름 : 위와 혼동 될 수 있는 코드이나 람다식 내부의 지역 변수(외부 three와 다름)

auto Lambda3 = [=](int x){ return x + three };
// = : Capture by Value. 값에 의한 캡쳐, 모든 외부 변수를 캡쳐함.

auto Lambda4 = [&](int x){ return x + three };
// & : Capture by Reference. 참조에 의한 캡쳐, 모든 외부 변수를 참조로 캡쳐함

auto Lambda4_1 = [&three](int x){ return x + three };
// &변수이름 : 특정 변수를 참조하여 사용
  • parameter(매개변수) :
    선택 사항. 람다식에 매개변수를 받고자 하면 작성한다.
auto max = [](int a, int b) { return a > b ? a : b; };
  • specifiers(지정자) :
    선택 사항. 캡쳐로 받아온 변수는 람다식 내에선 수정이 불가한데 이는 캡쳐한 변수가 람다식의 데이터 변수로 쓰일때 기본적으로 const로 설정 되기 때문이다. mutable을 지정하면 이를 non-const 할 수 있으며, mutable를 사용할 때는 매개변수를 사용하지 않더라도 ()를 반드시 적어야한다.
int a = 10;

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

auto changeValue() = [a]() mutable //추가
{
   cout << ++a << endl //ok
};

cout << changeValue() <<endl;
  • return_type(반환형) :
    선택사항. 지정 반환형으로 출력하고 싶을 때 사용. 변환 형을 적지 않으면 컴파일러가 추론한다.
auto Lambda1 = [](auto a, auto b)->int { return a + b };
// a = 8.5 , b = 3.3 -> 출력 : 11

auto Lambda2 = [](auto a, auto b)->auto { return a + b };
// auto 가능
// a = 8.5 , b = 3.3 -> 출력 : 11.8
  • body(함수식) :

 


람다(Lambda) == 클로져(Closure) ?

람다식을 찾아보면서 클로져에 대한 얘기가 많이 나오는데 이펙티브 책 시리즈 저자 Scott Meyers의 블로그 글을 인용하는 것으로 설명이 될 듯하다.

” In recent days, I’ve twice found myself explaining the difference between lambdas and closures in C++11, so I figured it was time to write it up.
최근, 람다와 클로져에 대한 차이점에 대해 두번이나 설명하고 있는 자신을 발견하고 이 글을 쓰기로 했다.

The term “lambda” is short for lambda expression, and a lambda is just that: an expression. As such, it exists only in a program’s source code. A lambda does not exist at runtime.
“람다”라는 것은 람다 표현식의 준말이고, 그저 표현식일 뿐이다.  그것은 단지 프로그램의 소스 코드에서만 존재한다.  런타임에서는 람다는 존재하지 않는다.

The runtime effect of a lambda expression is the generation of an object. Such objects are known as closures.
람다 표현식에 대한 런타임 결과는 오브젝트의 생성이다.  그러한 오브젝트를 클로져라고 한다. Given
아래 예를 보면, 

auto f = [&](int x, int y) { return fudgeFactor * (x + y); };the blue expression to the right of the “=” is the lambda expression (i.e., “the lambda”), and the runtime object created by that expression is the closure.
“=” 오른쪽에 있는 표현식이 람다이고, 이 표현식으로 부터 생성된 런타임 오브젝트가 클로져이다.

You could be forgiven for thinking that, in this example, f was the closure, but it’s not. f is a copy of the closure. 
위의 예에서 f를 클로져라고 생각할 수도 있지만, 그건 아니다. f는 클로져의 복사본이다.

The process of copying the closure into f may be optimized into a move (whether it is depends on the types captured by the lambda), but that doesn’t change the fact that f itself is not the closure. 
클로져를 복사하는 과정은 move로 최적화될 수 있지만 f가 클로져가 아니라는 사실은 변하지 않는다.

The actual closure object is a temporary that’s typically destroyed at the end of the statement.
실제 클로져 오브젝트는 임시 객제로 그 줄의 끝에서 파괴된다.

The distinction between a lambda and the corresponding closure is precisely equivalent to the distinction between a class and an instance of the class. 
람다와 클로져의 차이는 정확하게 클래스와 클래스 인스턴스의 차이와 동일하다.

A class exists only in source code; it doesn’t exist at runtime. 
클래스는 오직 소스코드에서만 존재하고, 런타임에서는 존재하지 않는다.

What exists at runtime are objects of the class type.  
런타임에서 존재하는 것은 클래스 타입의 오브젝트들이다.

Closures are to lambdas as objects are to classes. 
클로져와 람다의 관계는 오브젝트와 클래스의 관계와 같다.

This should not be a surprise, because each lambda expression causes a unique class to be generated (during compilation) and also causes an object of that class type–a closure–to be created (at runtime).”
놀라울게 없는게, 각 람다 표현식은 컴파일 과정에서 고유한 클래스를 만들어내고, 그 클래스 타입의 오브젝트(클로져)가 (런타임에서) 생성된다.

Lambdas vs. Closures from SCOTT MEYERS

http://scottmeyers.blogspot.com/2013/05/lambdas-vs-closures.html

'프로그래밍 > C & C++' 카테고리의 다른 글

[C++] - extern 변수  (0) 2023.05.23
[C++] - 캐스트 총정리  (0) 2023.05.16
[C++] - Static  (0) 2023.05.16
[C++] - 지역, 전역, 동적 변수의 차이점  (0) 2023.05.12
Comments