-
c++] 람다함수 사용하기프로그래밍/일반 2018. 7. 8. 18:22
c++ 람다함수 C++ 람다함수 사용하기
기본 구조
x[/*캡쳐할 변수*/](/*사용할 인자*/){/*함수내용*/};
기본적으로 람다 함수의 구조는 위와 같다.
[] 의 안에는 람다 함수의 바깥에서 사용되는 변수 중에서 람다함수 내에서 사용하고 싶은 변수를 지정하면 되고, () 안에는 일반적인 함수 선언 시 처럼 함수의 인자로 받아올 변수들을 선언하면 된다. {} 에는 당연히 함수의 실제 동작 코드를 작성하면 된다.
이처럼 작성된 람다함수는 생성 즉시 실행하고 해제하거나, 변수에 할당하여 일반 함수처럼 사용할 수 있다.
xxxxxxxxxx
[](int a) {printf("%d", a)} (10); // 10을 출력. 그 자리에서 람다함수를 사용
auto printer = [] (int a) {printf("%d", a)}; // 람다 함수를 변수에 할당
printer(10); // 10을 출력. 일반 함수처럼 사용함.
위의 코드 처럼 함수 선언 바로 뒤에
(넘겨줄 인자)
를 작성하여 바로 사용을 할 수도 있고, 변수에 직접 람다함수를 할당하여 유효 범위 내에서 함수처럼 사용할 수도 있다.캡쳐 변수
일반 함수와 람다함수에서의 큰 차이점은 캡쳐 변수를 사용한다는 점이다. 이는 일반 함수에서는 없던 구문으로 처음 람다함수를 사용하는 사용자들이 많이 헷갈려 하는 부분이다. 첫 사용자들을 위해 캡쳐 변수에 대해서만 상세하게 설명하도록 한다.
람다함수에서 사용하는 캡쳐 변수의 타입은 2가지이다.
- 값복사 캡쳐 변수: 일반적인 복사 방식으로, 변수의 값 전체를 복사해서 사용한다.
- 참조복사 캡쳐 변수: 값을 복사 하지않고 변수의 참조를 복사하여 사용한다.
캡쳐 변수 부분에 작성하는 코드는 아래와 같다.
x
[&](){printf("유효 범위 내의 모든 변수를 참조복사")};
[=](){printf("유효 범위 내의 모든 변수를 값복사")};
[](){printf("어떤 변수도 복사/참조 하지 않음")};
[&x](){printf("변수 x 만 참조 복사하여 사용함")};
[x](){printf("변수 x 만 값 복사하여 사용함")};
[&x, y](){printf("변수 x 는 참조 복사, y는 값복사 하여 사용함")};
위의 예제코드를 보면서 하나씩 확인해보자.
[]
에 들어갈 수 있는 코드 형태는 아래와 같다.- 아무 것도 작성하지 않음: 외부 변수를 참조하지 않는다.
&
: 모든 변수를 참조 복사 형태로 사용함.=
: 모든 변수를 값 복사 형태로 사용함.&변수이름
: 지정한 변수만 참조 복사 형태로 사용함.변수이름
: 지정한 변수만 값 복사 형태로 사용함.변수이름, 변수이름, &변수이름
: 다중 변수들을 지정한 형태 (값/참조) 로 사용함.
람다 함수 내에서 캡쳐 변수들을 사용할 때,
&
를 통해 복사한 변수들은 일반적인 참조 변수와 같이, 함수 내부에서의 변수 값 수정이 곧바로 원본 변수에 반영 된다.그러나
=
혹은변수이름
을 통해 복사한 변수들을 참조복사가 아니고 값복사 이므로, 함수 내에서 변수에 대한 수정을 가할 수 없다. 혹시나 함수 내에서x = 10;
등을 통해 값복사 변수에 새로운 값을 할당하려고 하면 컴파일 에러가 발생하게 된다.이 때, 반드시 람다 함수 내에서 값복사 캡쳐를 해야 하고, 함수 내에서 해당 변수를 수정할 일이 필요한 경우에는
mutable
지시자를 추가하여 컴파일 에러를 피할 수 있다.xxxxxxxxxx
auto f = [&x, y] () {
x = 10; // 정상적으로 컴파일 됨
y = 20; // 컴파일 에러가 발생함.
}
auto h = [&x, y] () mutable {
x = 10;
y = 20; // 컴파일이 정상적으로 수행됨. 원본 데이터에 반영은 되지 않음.
}
람다 함수를 저장하고 있는 변수의 변수 타입
위의 예제에서 람다함수를 변수에 할당 할 때
auto
키워드를 사용하여 변수 자체적으로 타입을 설정하도록 하였다.auto
가 아니라 람다함수의 타입을 직접 지정하고 싶은 경우에는#include <functional>
을 통해 지정할 수 있다.기본적인 타입 형태는 아래와 같다.
xxxxxxxxxx
// function<리턴타입(콤마로 구분한 인자의 타입들)>
function<void()> print = [](){}; // 리턴도 안하고 인자도 안받음
function<void(int)> print2 = [](int a){}; // 리턴은 안하지만 int 형 인자를 받음
function<int(int, int)> print3 = [] (int a, int b) {
return a + b;
} // int 형 리턴을 하며, int 형 인자 2개를 받음
람다 함수에서의 재귀 호출
람다 함수에서도 일반 함수에서처럼 재귀 호출을 사용할 수 있다. 단 재귀 호출을 사용할 때에는
auto
키워드를 사용할 수 없고,functional
을 통해 직접 함수 타입을 지정해야 한다. 또한, 람다함수 내에서 람다 밖에서 선언된 람다함수를 할당한 변수를 사용하고 있으므로, 캡쳐 변수에서 해당 변수를 캡쳐함을 명시해야 한다. 아래 예제를 통해 살펴보자xxxxxxxxxx
function<int (int)> f = [&] (int n) {
if (n == 0) return 1;
return f(n-1);
}
위는 간단히 람다 함수를 통해 재귀호출을 한 에제 이다.
인자로 int 형 한개를 받고, 리턴 값이 int 형 이므로
function<int(int)>
를 통해 함수의 타입을 명시하였다. 그리고 람다 함수 내부에서 변수f
호출을 통해 재귀 호출을 수행해야 하므로,&
캡쳐 변수를 통해 외부 변수인f
를 참조할 수 있도록 하였다.'프로그래밍 > 일반' 카테고리의 다른 글
[Git] git 비어있는 브랜치 생성하기 (0) 2018.07.26 Typora 마크다운 에디터 (1) 2018.03.02 Atom vim 모드로 사용하기 (0) 2017.10.23 아톰 설치하기 (0) 2017.10.22 C/C++ 옵션 파싱하기 (Option parsing) (0) 2016.04.07