Closure

마지막 수정 시각: 2020-10-26 22:03:12

원래 개념은 함수형 프로그래밍에서 나온 것으로, 간단히 설명하자면 자신이 처음 선언될 때의 컨텍스트(context)를 기억하고 있는 코드 묶음이라고 볼 수 있다. C++에서는 보통 람다와 같이 묶어서 이야기할 때가 많다.

간단하게 예를 들어 보자.

int a = 10;
int b = 20;

여기서 a+b를 가리키는 코드 묶음(closure) f를 생각해보자. 이 f는 현재 시점에서 30을 가리킬 것이다. 하지만 다른 작업을 하다가 a와 b의 값이 아래와 같이 바뀌었다고 하자.

a = 30;
b = 20;

이 시점에서 closure f를 호출하면 어떻게 될까? a+b=50이라고 출력될까? 아니다. closure는 자신이 선언된 시점의 컨텍스트를 기억하고 있기 때문에 자신이 선언된 시점의 a,b 값인 10,20을 더해 30을 출력한다.

이 closure의 기본 개념은 C++ 람다의 캡쳐를 통해 거의 유사한 방식으로 나타난다. 람다도 나중에 다룰 내용이니 간단하게만 살펴보자.

auto a = [](){std::cout << "bar" << std::endl;};

a(); //bar 출력

일반적으로 위와 같은 형태로 람다를 이용할 것이다. 이 때 [] 부분을 이용해서 해당 람다가 선언될 때의 컨텍스트를 기억시킬 수 있다. 처음에 예로 들었던 a+b를 기억하는 closure f를 한 번 구현해보자.

int a = 10;
int b = 20;

auto f = [a,b](){return a+b;};
f(); //30 출력.

[]안에 넣은 변수들은 캡쳐(capture)가 되어서 람다 내부에서 사용 가능하다. 즉 람다 밖의 값들을 람다 내부에서 이용하기 위해 사용하는 것이 캡쳐이다. 이 때 값으로 캡쳐하고 싶다면 그냥 이름을, 레퍼런스로 캡쳐하고 싶다면 이름 앞에 &를 붙이면 된다. 값으로 캡쳐할 경우 해당 람다가 선언될 때의 값이 기억되기 때문에 위에서 말한 closure의 개념과 완전 동일하다. 자신이 실행될 때의 context를 기억하는 코드 묶음이 되는 것이다.

int a = 10;
int b = 20;

auto f = [a,b](){return a+b;};
f(); //30 출력.

a = 20;
b = 30;

f(); //여전히 30 출력.

람다를 선언하면 내부적으로 임시 클로져 객체(closure object)를 만들어낸다. 이 클로져 객체가 생성될 때 캡쳐된 변수들을 내부적으로 저장하기 때문에 이런 식으로 사용이 가능한 것이다.

지금은 closure의 기본 개념에 대해서만 대충 이해하고 넘어가면 될 것 같다. 이 정도로 정리. 나중에 람다를 다루는 파트에서 더 자세히 공부해보자.