[C++] 람다식(functional) 사용법과 클로저(closure) 그리고 auto 자료형


Study/C , C++ , MFC  2020. 3. 22. 02:03

안녕하세요. 명월입니다.


이 글은 C++에서 람다식(functional) 사용법과 클로저(closure) 그리고 auto 자료형에 대한 글입니다.


사실 제가 10년전에 C++를 사용할 때는 람다식이 없었습니다. 아니면 있었는데 제가 사용법을 몰랐거나 잘 사용하던 시절이 아니었습니다.

그러다 제가 주력 프로젝트가 웹 프로젝트 비중이 많아지고, 그러면서 Javascript를 배우면서 함수 지향 프로그래밍의 매력을 느끼고 Java와 C#의 람다식을 익히게 되었습니다.

최근, 블로그 글을 작성하면서 예전에 자주 사용했던 언어 C++를 더 잊어버리기 전에 한번 정리해야겠다 생각하고 글을 작성하고 있습니다. 그러던 중 C++도 람다식이 있지 않을까 하고 조사하던 중 역시 C++도 람다식이 있었습니다.

링크 - https://en.cppreference.com/w/cpp/language/lambda

그것도 C++ 11부터 지원을 했네요...(C++ 11이면 10년 전인데....)


테스트 해보니 역시 C++에서도 람다식은 꽤 심플하고 이해만 잘되면 C++ 개발이 많이 편해질 것 같습니다. 참고로 람다식은 디자인 패턴의 옵서버 패턴과 관계가 있습니다.

링크 - [Design pattern] 옵서버 패턴 (Observer pattern)

#include <stdio.h>
#include <iostream>
using namespace std;
// 실행 함수
int main()
{
  // 람다식은 []로 시작해서 람다식에 들어갈 파라미터 설정 그리고 화살표, 리턴 데이터 타입 설정한다.
  // stack 영역은 람다의 실행 영역이다.
  auto lambda = [](int a, int b) -> int { return a + b; };
  // 콘솔 출력
  cout << " 1 + 2 = " << lambda(1, 2) << endl;
  return 0;
}

C#에서는 Action이나 Func 함수가 있지만, C++에는 없습니다. 대신 auto가 있습니다. 이 auto함수는 타입 추론 방식입니다. C#에서는 var타입과 같습니다.

(void*)와는 조금 타입의 차이가 있습니다. void*는 할당된 객체의 메모리 주소라면 auto는 타입의 추론입니다.

즉, int형이 될 수도 있고, float가 될 수도 있습니다. 대신 한 번 추론이 된 타입은 변경이 되지 않습니다.

람다식은 사실 데이터 타입이 조금 복잡합니다.

그래서 변수로 선언할 때는 auto로 선언합니다. 즉, 람다식과 auto 타입은 같이 붙어다니는 타입입니다.


람다식을 위처럼 사용하지는 않습니다. 저렇게 쓸 바에야 함수를 만드는게 낫습니다.

옵서버 패턴을 위한 것입니다.

#include <stdio.h>
#include <iostream>
using namespace std;
// 옵서버 패턴의 함수 (람다식의 설정에 따라 결과값이 바뀐다.)
// 데이터 타입은 return 값이 int형이고 파라미터는 int, int가 있는 람다식이다.
void print(int (*param)(int, int))
{
  // 콘솔 출력
  cout << " param(1 , 2) = " << param(1, 2) << endl;
}
// 실행함수
int main()
{
  // print 함수에 a + b의 람다식을 넘긴다.
  print([](int a, int b) -> int { return a + b; });
  // print 함수에 a - b의 람다식을 넘긴다.
  print([](int a, int b) -> int { return a - b; });
  return 0;
}

여기서 람다식을 함수의 파라미터로 받을 때는 함수 포인터로 람다식을 받습니다. auto는 함수에서 파라미터의 자료형으로 사용할 수 없기 때문입니다.


람다식이 나왔으니 클로저(closer)의 개념을 설명하겠습니다.

클로저는 사실 람다 함수의 밖에서 선언된 변수를 사용하는 기능이 클로저입니다.

#include <stdio.h>
#include <iostream>
// 람다식을 위한 해더 선언
#include <functional>
using namespace std;

// 위에서는 함수 포인터를 사용했지만, functional를 이용하면 더 쉽게 람다식을 파라미터로 받을 수 있다.
// 리턴값(파라미터)
void print(function<int(int)> param)
{
  // 콘솔 출력
  cout << " param(1 , 2) = " << param(1) << endl;
}
// 실행 함수
int main()
{
  // 변수 선언
  int test = 10;
  // 클로져를 사용하기 위한 방법
  // &는 참조 방식으로 람다식 안에서 외부 변수의 값을 취득, 수정할 수 있다.
  print([&](int a) -> int { return test + a; });
  // =는 캡쳐 방식으로 람다식 안에서 외부 변수의 값을 취득은 가능하나 수정은 불가능하다.
  print([=](int a) -> int { return test - a; });
  return 0;
}

위에서 클로저를 [=]와 [&]를 넣어서 사용했습니다.

그러나 사양에 따라서는 전체 변수의 캡쳐가 아닌 특정 값만 클로저 기능을 사용하고 싶을 때도 있습니다.

[test]를 써서 특정 변수 캡쳐나 [&test]를 써서 변수 참조를 사용할 수 있습니다.

여기까지 C++에서 람다식 사용법에 대한 글이었습니다.

궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.