본문 바로가기

C++

C++11 람다 함수 (Lambda Function)

람다함수를 대략적으로는 알고있었지만

쓰질않아서 정확히는 몰랐다.

최근 읽은 책에서 자세히 설명이 되어있어서 읽은김에 정리를 좀 해봐야겠다.

 

 

이러니 저러니 설명보다 일단 예제코드다.

#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 Sum0이 출력되었다.

 

람다함수의 원형을 다시 정리해보면

 

        [&] (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