람다함수를 대략적으로는 알고있었지만
쓰질않아서 정확히는 몰랐다.
최근 읽은 책에서 자세히 설명이 되어있어서 읽은김에 정리를 좀 해봐야겠다.
이러니 저러니 설명보다 일단 예제코드다.
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
// 준비사항
std::vector<int> vecNumber{1, 2, 3, 4, 5};
// 숫자 3을 살펴보자.
auto nThrees_Count = std::count(vecNumber.begin(), vecNumber.end(), 3);
std::cout << "Num3 Count : " << nThrees_Count << std::endl;
// 3보다 큰 수를 찾자.
auto isAbove_3 = [](int i) { return i > 3; };
auto nAbove_Count = std::count_if(vecNumber.begin(), vecNumber.end(), isAbove_3);
std::cout << "Num3 Above Count : " << nAbove_Count << std::endl;
}
// 출력결과
Num3 Count : 1
Num3 Above Count : 2
auto isAbove_3 = [](int i) { return i > 3; };
이부분이 람다 함수다.
사용방법은 보통 함수포인터와 비슷해 보인다.
auto를 쓰니까 자료형은 정확히 어떻게 명시해줘야할지 잘 모르겠다.
좀더 사용방법을 알아보자
std::vector<int> vecNumber{1, 2, 3, 4, 5};
int nCheckNum = 3;
// 3보다 큰 수를 찾자.
auto isAbove_3 = [nCheckNum](int i) { return i > nCheckNum; };
auto nAbove_Count = std::count_if(vecNumber.begin(), vecNumber.end(), isAbove_3);
std::cout << "Num3 Above Count : " << nAbove_Count << std::endl;
// 출력결과
Num3 Above Count : 2
auto isAbove_3 = [nCheckNum](int i) { return i > nCheckNum; };
[ ] 대괄호 안에 지역변수 nCheckNum이 추가되었다.
그리고 그 값을 람다함수 안에서도 사용이 가능한가보다.
이 대괄호는 캡쳐블록(Capture block)이다.
캡쳐블록은 외부변수를 참조할때 쓰인다.
함수 파라미터에 레퍼런스를 넣어주는 것처럼 캡쳐블록도 레퍼런스로 넣는것도 가능하다.
람다함수는 실질적으로 클래스를 생성해주는것과 비슷한 효과를 가진다.
class isAbove_3
{
public:
isAbove_3(int nCheckNum)
: m_nCheckNum{ nCheckNum }
{
}
auto operator()(int i)
{
return i > m_nCheckNum;
}
private:
int m_nCheckNum{};
};
위의 람다함수를 클래스로 묘사한 모습이다.
익숙한 클래스로 바꿔보니 쉽게 이해되는기분이다.
캡쳐블록의 캡쳐방식을 마저 소개해본다.
코드 | 결과 |
int a, b, c; auto func = [=]() {}; |
a, b, c를 Value로 캡쳐 |
int a, b, c; auto func = [&]() {}; |
a, b, c를 Reference로 캡쳐 |
int a, b, c; auto func = [=, &c]() {}; |
c만 Reference로 캡쳐, 나머지 Value로 캡쳐 |
int a, b,c; auto func = [&, c]() {}; |
c만 Value로 캡쳐, 나머지 Reference로 캡쳐 |
=이나 &를 캡쳐하면 해당 지역변수를 모두 캡쳐해온다.
람다함수는 캡쳐한 값들, 멤버변수를 바꿀때엔 반드시 명시적으로 mutable 키워드를 사용해 주어야 한다.
반대로 말하면 기본적으로 람다함수는 const 함수로, 멤버변수를 바꾸지 않게 설계되어 있다.
std::vector<int> vecNumber{ 1, 2, 3, 4, 5 };
int nCheckNum = 3;
int nSum = 0;
// 3보다 큰 수의 합을 구하자
auto isAbove_3 = [&](int i)-> bool { if (i > nCheckNum) nSum += i; return i > nCheckNum; };
auto nAbove_Count = std::count_if(vecNumber.begin(), vecNumber.end(), isAbove_3);
std::cout << "Num3 Above Count : " << nAbove_Count << std::endl;
std::cout << "Num3 Above Sum : " << nSum << std::endl;
// 출력결과
Num3 Above Count : 2
Num3 Above Sum : 9
이번 예제에는 추가로 리턴타입도 명시해보았다. (->bool)
명시하지 않으면 자동으로 생성되는듯하다.
그런데 Reference [&]로 캡쳐했더니, mutable을 빼도 에러가 나지 않는다???
기존 const 함수와 마찬가지로.. 레퍼런스나 포인터를 참조한 데이터를 변경하는것은 컴파일 에러가 검출되지 않는다.
...
std::vector<int> vecNumber{ 1, 2, 3, 4, 5 };
int nCheckNum = 3;
int nSum = 0;
// 3보다 큰 수의 합을 구하자
auto isAbove_3 = [&](int i)-> bool
{
if (i > nCheckNum)
{
nSum += i;
std::cout << "nSum:" << nSum << std::endl;
}
return i > nCheckNum;
};
auto nAbove_Count = std::count_if(vecNumber.begin(), vecNumber.end(), isAbove_3);
std::cout << "Num3 Above Count : " << nAbove_Count << std::endl;
std::cout << "Num3 Above Sum : " << nSum << std::endl;
// 출력결과
nSum:4
nSum:9
Num3 Above Count : 2
Num3 Above Sum : 0
mutable 키워드를 붙여야만 Value로 캡쳐한 멤버변수 nSum을 변경할수 있다.
Value로 캡쳐했기 때문에 Num3 Above Sum 은 0이 출력되었다.
람다함수의 원형을 다시 정리해보면
[&] (int i) mutable -> bool { ; }
[캡쳐] ( 전달인자 ) 한정자 -> 리턴타입 { 구현부; }
이정도로 정리하면 될것같다.
'C++' 카테고리의 다른 글
C++11 stl에 추가된 emplace(insert), emplace_back(push_back) (0) | 2020.04.17 |
---|---|
C++11 가변인자 템플릿 (Variadic template) (0) | 2020.04.12 |
C++11 enum class (1) | 2020.04.12 |
유니코드 문자열 처리 (0) | 2020.04.12 |
printf문의 서식지정자 (0) | 2020.04.12 |