Language/C++

C++ 11 :: Lambda Expression (람다 표현식) + 함수 객체 (Functor)

VallistA2015. 2. 14. 17:42

C++ 11에 람다 표현식이 생겼다.

먼저, C++ 11에 추가된 람다가 뭐하는것인지 알아야한다.

대충 배경은 이렇다.. 

사람들은 언제나 조금이라도 불편한 것을 해결하려는 방향으로 일을 진행하는데.. 람다의 탄생 배경에는 함수 객체 (Function Object of Functor) 라고 불리는 개념이 연관되어 있다.

좀 더 쉽게 말하자면 함수 객체를 사용하는데 불편함을 느낀 사람들이 있어서 람다가 만들어졌다.

그리고 람다의 개념은 java나 c#등 타 언어에도 있을만큼 많이 쓰인다.


함수 객체는 C++ 11에 새롭게 추가된 기능이 아니라 기존부터 있었음. (람다가 추가되었다.)

함수 객체는 객체를 마치 함수처럼 사용하기에 붙여진 이름이고 함수가 되기위한 조건중 하나는 괄호인데, (함수 호출 연산자 Function call operator)를 사용해 파라미터 목록을 받는 것이다. 어떤 객체를 함수처럼 사용한다는 것은 객체에 괄호를 붙여서 마치 겉보기엔 함수를 호출하는 것처럼 사용한다는 의미이다.


하지만 아무 객체에나 괄호를 붙여준다고 컴파일러가 이를 이해를 하는가? 

컴파일러가 이해할 수 있게 하려면 클래스 안에서 함수호출 연산자를 오버로딩 해주어야 한다.


먼저 함수 객체의 개념을 알도록 하자 (Functor)


1.



2.


구조체는 모든게 public인 C++ 클래스로 간주 가능하다. ( 구조체를 사용하여 인터페이스 만들기도 한다. )

함수객체용 클래스 만드는 방법은 의외로 간단한데, 클래스를 정의할 때 내부에 함수 호출 연산자만 오버로드 해서 정의를 해주면 되기 때문이다.



함수 객체를 왜 알아야 하는가를 적자면.

STL의 많은 알고리즘이 함수 객체를 즐겨 사용을 하기 때문에 더더욱 알아야 하며

이게 과연 특징이라고 볼 수 있냐는 의견도 있지만, 분명 STL 알고리즘에서는 함수 객체를 많이 사용하기 때문에, 적어도 알아야 할 개념이다.

먼저 STL에서 정의하는 함수 객체 타입에 대해 이해를 해야하는데, STL에서는 함수 객체를 세가지 타입으로 분류를 하는데, 이들 각각에 다른 이름을 사용한다. 

foo() 함수처럼 인자 없이 호출되는 함수객체는 발생자(Generator) 라고 부르고

foo(x) 처럼 하나의 인자를 받는 것을 단항 함수 (Unary Function) 이라고 부른다.

foo(x,y)처럼 두 개의 인자를 받는 것을 이항 함수 (Binary Function) 이라고 부른다.

인자의 개수와는 별도로 bool 값을 반환하는 파라미터가 함수 포인터나 함수 객체라면 술어 (Predicate) 라고 부른다.

단항 함수가 불 값을 반환하는 경우는 단항 술어 (Unary Predicate) 또는 술어 (Predicate) 라는 이름으로 부르고, 이항 함수가 불 값을 반환하는경우엔 이상 술어 (Binary Predicate) 라고 부른다.

세개 이상은 STL에서 존재하지 않기 때문에 그런말은 사용하지 않는다.

우리가 알아야 할 것은 단항 술어 (Unary Predicate) 이항 술어 (Binary Predicate) 이 두가지 경우만 알아두면 된다.


3.


4.


람다

함수 객체를 설명한 이유는 함수 객체가 람다와 목적이 동일하면서도, 이를 한층 업그레이드 한 개념이기 때문이었다.

함수 객체와 비교했을 때 람다가 갖는 장점 가운데 두 가지만 꼽아본다면 첫 번재는 직접 코드를 개발하는 입장에서 코딩이 간편해진다는 점, 두번째는 다른 사람이 작성한 코드를 읽을 때 코드의 가독성이 향상된다는 것이다.

굳이 두가지 장점으로 나누었지만 곰곰이 생각해보면 이 두가지 장점은 내가 작성한 간결한 코드는 남이 읽기도 쉽다는 하나의 사실로 귀결된다.

프로그래머는 자신이 작성한 코드만 읽는 사람이 아니라 다른 사람이 작성한 코드도 항상 마주해야하는 직업이므로 이는 아주 중요한 장점이 된다.


자, 람다 예제를 보도록 하자.


1.



2.



3.


대충 람다 표현식을 보아봤는데, 이제 다시 설명에 들어가도록 하겠다.


람다의 사용법은 간단하다. 

- []{} 

- [](인자){}


람다 소개자 : [] (Lambda Introducer)

파라미터 지정자 : () (Parameter Specifier)

람다 몸체 : {} (Lambda Body)


이 두가지 방법중 하나를 사용하면 되는데, 람다 표현식에 변수를 넘기고 싶은 경우에는 가운데 괄호를 넣고 인자를 넣으면 된다.


람다 함수는 람다 소개자로 시작해야 인식을 하는데, 람다 소개자라는 것은 바로 맨 처음 대괄호를 지칭한고 파라미터 지정자는 람다 함수에 넘겨줄 인자를 입력할 수 있는 메서드다. 그리고 람다 몸체는 일반 함수와 쓰임새가 같으며, {} 내부에 람다 함수가 수행할 일을 작성한다.


그리고 맨 뒤에 ()를 붙이고 인자를 넣어주면 함수가 시작이 된다.


4.



[] 는 캡처 절 이라고도 하는데, 캡처의 사전적 의미를 찾아보면, "Take into one's possession or control by forse" 라고 되어있다.

남의 것을 자기 것으로 만들거나 마음대로 조종하는 것을 뜻한다. 람다 함수에서 캡처의 대상은 람다 함수 외부에 선언된 (하지만 람다 함수를 감싸는 범위안의) 변수 이다.


5.



[&] 라는 것이 보일 것 이다. 이것은 람다 외부에서 선언된 모든 변수를 람다 함수 내부에서 레퍼런스 타입으로 캡처 (Capture by reference) 해 사용할 수 있도록 하고 있다. (즉 람다 함수 외부에서 선언된 변수를 가져다 쓸 수 있다는 것이다)


그러므로 이 두 변수를 람다 함수 내부에서 따로 정의하지 않았지만 사용할 수 있었던 것이다.

또한 레퍼런스 타입으로 캡처했기 때문에 람다 내부에서 작성한 변경 사항은 그대로 원래 변수 값으로 반영된다.


레퍼런스 타입으로 캡처하는 방법 외에 값으로 캡처 (Caputre by value) 할 수도 있다.

외부 변수를 간단ㅁ히 넘겨봐야 이를 적용한 연산만 수행할 때는 값으로 캡처하는 편이 좋다. 단 이때는 &를 붙이지 않는 상태로 작성한다. 가령 Caputre By Value로 넘겨 받으려고 하면. [&] 가 아닌 [evenSum, oddSum] 으로 해도 된다.


다만 밑의 구문에서는 Reference로 받아야 값 연산이 가능해지므로 (주소 값 직접 조작) & 이 옳다.


다음은 속성 값을 알아보도록 하자.


[] : 아무것도 캡처하지 않음

[&x]: x만 Capture by reference 

[x] : x만 Capture by value

[&] : 모든 외부 변수를 Capture by reference

[=] : 모든 외부 변수를 Capture by value

[x,y] : x,y 를 Capture by value

[&x,y] : x는 Capture by reference , y는 Capture by value

[&x, &y] : x,y 를 Capture by reference

[&, y] : y 를 제외한 모든 값을 Capture by reference

[=, &x] : x 를 제외한 모든 값을 Capture by value


static 변수는 캡처할 수 없고 Automatic storage duration을 갖는 일반변수만 가능하다.


값으로 캡쳐된 외부 변수는 const의 특성을 가지게 되어, 람다 함수 몸체에서 값에대한 수정이 불가능해진다.

그러나 mutable 이라는 키워드 (mutable은 storage class specifier 스토리지 클래스 지정자) 를 이용하여 수정이 가능하다.


6.



람다 함수는 auto 키워드를 이용해 함수 자페를 하나의 변수에 할당할 수 있고, 또한 람다 함수 내부에서 또 다른 람다 함수를 호출 할 수도 있다.


7.



'Language > C++' 카테고리의 다른 글

C++ 11 :: R-Value Reference  (0) 2015.02.14
C++ 11 :: static_assert Keyword  (0) 2015.02.14
C++ 11 :: Smart Pointer (Shared_ptr, Unique_ptr, Weak_ptr)  (3) 2015.02.14
C++ 11 :: Array  (0) 2015.02.13
C++ 11 :: decltype  (0) 2014.10.20

댓글

VallistA

병특이 끝나서 게임에서 웹으로 스위칭한 프로그래머.
프로그래밍 정보등을 공유합니다.
현재는 이 블로그를 운영하지 않습니다.
vallista.kr 로 와주시면 감사하겠습니다!

자고 싶습니다. ㅠㅠ

Github      :: 링크

궁금한점 문의 주시면 답변드리도록 하겠습니다

VISITED

Today :

Total :

SNS

  • 페이스북아이콘
  • 카카오톡아이콘
  • 트위터아이콘

Lately Post

Lately Comment