2015년 6월 8일 월요일

[C++11] 람다 표현식

람다의 장점
코딩을 하다보면 종종 함수 포인터나 함수 객체(Class 변수를 함수처럼 사용)를 인자로 넘겨줘야 하는 경우가 있습니다. 이러한 경우 기존에는 함수를 별도로 선언하거나 Class를 정의해서 사용해야 했습니다.
다른 곳에서 사용하지 않고 그곳에서만 한번 사용하는 경우 별도의 선언은 코딩 양을 늘리고 코드를 읽을때 그 함수를 쫓아가서 확인해야 하는 수고가 있습니다.
람다는 함수 인자가 필요한 곳에서 바로 함수를 선언함과 동시에 인자로 넣을 수 있는 장점이 있습니다. 이로 인해 코드의 양을 줄이고 가독성을 높일수가 있습니다.
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);

std::for_each(vec.begin(), vec.end(), [](int val){
std::cout << val << std::endl;
});
빨간색 부분이 람다 함수입니다. 

람다 함수
람다 함수 원형은 []{} 입니다. []는 람다 소재자이고 {}는 람다 몸체입니다. [](){} 처럼 ()를 추가하여 일반 함수처럼 인자를 전달할수도 있습니다. 람다 함수 몸체를 한번 채워보겠습니다.
[]{ std::cout << "Hello World" << std::endl; }; 

많 이 보았던 Hello World를 출력하는 함수입니다. 하지만 이렇게만 코딩하면 실제로 출력이 되지 않습니다. 람다 함수를 선언만 하고 호출은 하지 않았기 때문입니다. 람다 함수를 호출하려면 다음과 같이 뒤에 ()를 붙여주면 됩니다.
[]{ std::cout << "Hello World" << std::endl; }(); 


인자를 넘기려면 앞의 ()에 일반 함수와 같이 인자를 정의하고 뒤의 ()에 넘겨줄 인자를 넣으면 됩니다.
[](int i){ std::cout << "Argument "<< i <<std::endl;}(5);

이렇게 하면 Argument 5 가 출력됩니다.

반환 타입은 일반 함수와 달리 -> 기호를 이용해서 지정합니다.
 bool isTrue = []()->bool{ return true; }();

반환 타입을 지정하지 않았을 경우에는 함수 내부의 결과값을 가지고 컴파일러가 자동으로 추정합니다. 만약 컴파일러가 추정하지 못할 경우 void 타입으로 추정합니다.

람다 함수의 장점 중 하나가 람다 함수 외부의 변수를 바로 참조할수 있다는 것입니다. 람다 소재자인 []가 그 역할을 하는데 캡처절이라고도 부릅니다.
int a = 1;
int b = 2;

int c = [&]{ return a + b; }();
std::cout << "Result " << c << std::endl;

실행해보면 Result 3 이 출력됩니다. [&]가 람다 함수 외부 변수를 참조 타입으로 캡처했기 때문입니다.
참 조 타입이 아니라 값으로 캡처할 수도 있는데 차이점은 참조 타입으로 캡처했을 경우 람다 함수 내부에서 값을 변경하면 함수 외부에도 적용된다는 점이고 값으로 캡처했을 경우 외부에 적용이 되지 않는다는 점입니다. 이 부분은 일반 함수와도 같습니다.
이 외에도 여러가지 캡쳐 규칙이 있습니다.
[]: 아무것도 캡처하지 않음
[&]: 모든 외부 변수를 참조로 캡처
[&x]: x만 참조로 캡처하고 다른 변수는 캡처하지 않음
[&x, &y]: x, y만 참조로 캡처하고 다른 변수는 캡처하지 않음
[=]: 모든 외부 변수를 값으로 캡처
[x]: x를 값으로 캡쳐
[x, y]: x, y만 값으로 캡처하고 다른 변수는 캡처하지 않음
[&x, y]: x를 참조로 캡처하고 y는 값으로 캡처

한 함수 내에서 람다 함수를 선언하고 이를 여러변 사용해야 할 경우에는 auto 키워드를 사용하여 람다 함수를 변수로 할당하고 호출하면 됩니다.
auto printValue = [](int i){ std::cout << i << std::endl; };
printValue(1);
printValue(2);

댓글 없음:

댓글 쓰기