밑 글에 이어, C++ 0x lambda를 사용한 closure 예제

바로 전 글에 이어, 두 개의 예제 차이(?)를 들어보겠다.

class SomeObject {
  int m_value;
  public:
  SomeObject() : m_value(0) {}
  int Get() const { return m_value; }
  void Reset() { m_value = 0; }
  void Increment(int addend) { m_value += addend; }
};

이런 클래스의 객체 so가 있고. 이 so의 Increment() 멤버 함수를 인자로 지역 변수 x를 넣어, 미래의 특정 시점에 호출하려고 Closure로 만든다고 치자. 우선 기존 구현체를 쓴다면 다음과 같은 코드가 필요하다.

MakeClosure(so, &SomeObject::Increment, x);

이번엔 lambda를 이용해서 만든다고 치자.

MakeLambdaClosure([&so, x](){ so.Increment(x); });

인자가 하나 뿐이기에 큰 차이는 나지 않는다.

하지만 인자가 많아지고, 인자 중 일부는 복사하는 경우 비용이 매우 큰 경우라고 가정하자. 그러면 MakeClosure() 구현을 효율적으로 만드는 게 별로 쉬워지지 않는다.

그리고 좀 더 생각을 바꿔서(?) Increment() 함수를 노출시키고 싶지 않은 경우, lambda 를 사용하는 경우엔 MakeLambdaClosure를 SomeObject 내부의 스코프에서 호출하기만 하면 되지만, 전자는 이게 불가능하다. Closure 템플릿에서 해당 함수를 호출할 수 있어야만 템플릿 인스턴스가 만들어지기 때문이다.

그리고 Increment()를 1번 호출하는 게 아니라 k번 호출하는 경우를 생각해보자. 전자는 SomeObject의 멤버 함수를 새로 만들어야 한다. 반면에 후자는 다음과 같이 수정하면 끝이다.

MakeLambdaClosure([&so, x, k](){ for(int i = 0; i < k, ; ++i) so.Increment(x); });

C++ 0x에 추가된 lambda 문법이 access control, 기존 변수 접근하는 법(capture rule이라 부름), 함수를 직접 “만들어낸다”는 점에서 이것 저것 제어할 수 있어서 상당한 편의를 주게된다. 물론 다른 방법으로도 이 구현은 할 수 있기에 표현력이 증가하지는 않는다.