Intro
오늘은 STL 람다에 대해 알아보도록 하겠습니다. 람다는 저도 써본 적이 없고 솔직히 회사 생활하면서 누가 구현한 걸 본 적도 없었습니다.. 그냥 어떤 거구나 정도만 알고 있었던 기능입니다.
실제로 써본적은 없지만 이번에 공부하며 잘만 활용한다면 괜찮은 기능 이 될 것 같습니다(과연... 잘 활용할 수 있는지 의문입니다).
그럼 STL 람다에 대해 자세히 알아 보도록 하겠습니다.
람다(lamda)
람다는 람다 함수 또는 이름 없는 함수라고 불립니다. 그 성질은 함수 객체와 비슷하다고 생각하시면 될 것 같습니다.
람다의 장점
- find_if, sort 등의 알고리즘을 사용할 때 특정 조건자를 사용하려면 함수 객체를 선언해야 하는데 람다를 사용하면 객체를 선언할 필요가 없다.
- 코드의 길이가 줄어든다.(객체를 선언할 필요가 없기 때문에 코드의 길이가 줄어든다.)
단점
- decltype와 sizeof에서 사용이 불가능하다.
람다의 기본 구조
#include<iostream>
int main(void)
{
[]// 람다 캡쳐
()// 함수의 인수 정의
{}// 함수의 본체
();// 함수의 호출
return 0;
}
Example
지난 시간에 스터디한 auto를 활용하여 람다 변수를 대입할 수 있습니다.auto가 편하긴 편하네요...
#include<iostream>
int main(void)
{
auto func = []() {std::cout << "anabebe" << std::endl;};
func();
return 0;
}
결과
일반 함수처럼 파라미터도 넘길 수 있습니다.
Example Code
#include<iostream>
int main(void)
{
auto func = [](int Num) {std::cout << Num << std::endl;};
func(99);
func(999);
func(9999);
return 0;
}
결과
람다는 변환값(return)을 넘길 수도 있는데, 변환 값(return)의 형은 명시적으로 지정할 수도 있고 암묵적으로 추론이 가능합니다. 그냥 함수처럼 반환 값을 넘기는데 형을 이건 float로 반환해라, 이건 int로 반환해라 할 수도 있고 값을 넘기면 알아서 추론이 가능하게 암묵적으로 추론도 가능하다는 뜻입니다.
Example Code
#include<iostream>
int main(void)
{
auto func1 = []() {return 3.14;};
auto func2 = [](float f) {return f;};
auto func3 = []()->float {return 3.14;};
float f1 = func1();
float f2 = func2(3.14f);
float f3 = func3();
return 0;
}
람다 캡처
람다를 사용할 때 외부에 정의되어 있는 변수를 내부로 사용하고 싶을 면 그 변수를 캡처하면 됩니다. 캡처는 참조나 복사로 전달이 가능하고, 참조할 때는 &를, 복사로 전달할 때는 그냥 변수 이름을 대입하면 됩니다.
보통 C++에서 사용할 때 Call by Value & Call by Reference를 생각하시면 될 것 같습니다. 흠.. STL을 공부하시면 개념은 알 거라고 생각하고.. 넘어가도록 하겠습니다.
Example Code
#include <iostream>
#include <vector>
#include <algorithm>
int main(void)
{
std::vector<int>Num;
int nSum = 0;
for (int i = 1;i <= 10;i++)
Num.push_back(i);
std::for_each(Num.begin(), Num.end(), [&nSum](int sum) {nSum += sum;});
std::cout << "Sum = " << nSum << std::endl;
return 0;
}
vector Num에 1~10까지의 숫자를 넣고 for_each함수에 람다 함수를 넣고 캡처 기능을 써서 nSum에 vector값을 더해 줬습니다. 만약 &nSum을 그냥 nSum으로 복사로 캡쳐를 하게 되면 컴파일 에러가 납니다. 이 때 std::for_each(Num.begin(), Num.end(), [=](int sum)mutable {nSum += sum;}); mutable를 추가 하게 된다면 컴파일 에러 없이 사용이 가능합니다. 하지만 복사로 캡쳐를 했기 때문에 람다 내부에서 변경한 외부 변수의 값은 람다를 벗어나면 원래 값인 0으로 출력이 됩니다.
std::for_each(Iterator first, IIterator last, Function fn);
first부터 last전까지 원소들의 각각에 대해 fn을 실행합니다.
Default 캡쳐
람다는 외부의 모든 변수를 참조로 캡처하고 일부는 복사 또는 참조로 캡처가 가능합니다.
int n1, n2, n3, n4, n5;
[&, n1, n2] {};//n3,n4,n5는 참조, n1,n2는 복사
[=, &n1, &n2] {};//n3,n4,n5는 복사, n1,n2는 참조
예를 들어 람다를 [&, n1, n2] {};으로 선언을 했을 때 n1와 n2는 복사로 캡처를 하고, 맨 앞에 &을 선언함으로 나머지 n3,n4,n5는 참조로 캡쳐를 한다는 의미입니다. 반대로 [=, &n1, &n2] {};했을 때 n1과 n2는 참조로 캡쳐를 하고, =을 선언함으로 나머지 n3,n4,n5는 복사로 캡쳐를 한다는 의미입니다.
마무리
람다는 공부하며 느낀 람다의 가장 큰 장점은 STL 중에 find_if나 sort, 그리고 아까 사용했던 for_each 등 알고리즘을 사용할 때
특정 조건자를 사용하려면 함수 객체를 따로 정의해야 했는데, 람다를 사용하니 그 부분이 줄어들었습니다. 확실히 코드의 길이는 줄어듭니다. 하지만 아직 숙련이 부족해서 그런지 익숙하지 않네요.... 분명 잘 활용하면... 좋을 것 같긴한데...
이상으로 포스팅을 마치겠습니다. 감사합니다.
'[C++ STL]' 카테고리의 다른 글
[C++ STL - forward_list] (0) | 2021.06.24 |
---|---|
[STL - 메모리 관리(스마트 포인터 shared_ptr, unique_ptr] (0) | 2021.06.08 |
[C++ STL - tuple] (0) | 2021.05.21 |
[C++ STL - range based for] (0) | 2021.05.13 |
[C++ STL - auto] (0) | 2021.04.28 |