[C++ STL] Lambda, 람다

1. 람다(Lambda), 이름없는 함수

기존에는 find_if, sort 등의 STL의 알고리즘 사용 시, 함수 객체를 정의해야 했는데 함수 객체 정의를 할 필요가 없어졌음

2. 람다의 기본 사용 방법
[캡쳐](파라미터)->반환형{함수정의}(호출);

3. 람다 기본 사용 예시
#include <iostream>

void main()
{		
	auto func = [] { std::cout << "Hello, World" << std::endl; };
	func();

	system("pause");
}

4. 파라미터 예시

람다는 일반 함수 처럼 파라미터를 정의할 수 있음

auto func2 = []( int iX ) { std::cout << "print : " << iX << std::endl; };
func2( 111 );
func2( 222 );
func2( 333 );

5. 반환값 예시

람다는 반환값을 넘길 수 있음, 명시적, 암묵적 둘 다 가능

auto func3 = []()->float { return 3.14f; };
std::cout << func3() << std::endl;

auto func4 = [] { return 3.14; };
std::cout << func4() << std::endl;

auto func5 = [](float f) { return f; };
std::cout << func5(3.14f) << std::endl;

6. 캡처 예시

람다외부에 정의되어있는 변수를 내부에서 사용하고 싶으면 캡처 한다.

  • [&] : 람다 외부 변수 모두 참조로 캡처
  • [=] : 람다 외부 변수 모두 복사로 캡처
  • 변수 하나만 캡처 시 아래와 같이 사용
int iExam1 = 0;
auto func6 = [iExam1] { std::cout << "iExam1 : " << iExam1 << std::endl; }; // 복사로 캡처
func6();

auto func7 = [&iExam1] { std::cout << "iExam1 : " << iExam1 << std::endl; }; // 참조로 캡처
func7();

참초로 캡처 시 해당 변수를 람다 내부에서 변경 가능

int iTotal = 0;
auto func8 = [&] { iTotal++; std::cout << "iTotal : " << iTotal << std::endl; };
func8();

복사로 캡처 시 해당 변수를 람다 함수 내부에서 변경하면 아래와 같이 에러 발생

auto func9 = [=] { iTotal++; std::cout << "iTotal : " << iTotal << std::endl; };
func9(); // error C3491: 'iTotal': 변경 불가능한 람다에서 복사 방식 캡처를 수정할 수 없습니다.

복사로 캡처한 변수를 람다 내부에서 변경해야 한다면 아래와 같이 mutable 사용

auto func9 = [=] () mutable{ iTotal++; std::cout << "iTotal : " << iTotal << std::endl; };
func9();

아래와 같이 혼합해서 사용 가능

int iNum1 = 1, iNum2 = 2;
auto func10 = [iNum1, &iNum2]() mutable { std::cout << "iNum1 : " << iNum1 << " iNum2 : " << iNum2 << std::endl; };
func10();

전체 코드

#include <iostream>
#include <functional>
#include <algorithm>

void main()
{
	std::function<void(void)> func1 = []{ std::cout << "Hello, World" << std::endl; };
	func1();
		
	auto func = [] { std::cout << "Hello, World" << std::endl; };
	func();
	
	auto func2 = []( int iX ) { std::cout << "print : " << iX << std::endl; };
	func2( 111 );
	func2( 222 );
	func2( 333 );

	auto func3 = []()->float { return 3.14f; };
	std::cout << func3() << std::endl;

	auto func4 = [] { return 3.14; };
	std::cout << func4() << std::endl;

	auto func5 = [](float f) { return f; };
	std::cout << func5(3.14f) << std::endl;

	int iExam1 = 0;
	auto func6 = [iExam1] { std::cout << "iExam1 : " << iExam1 << std::endl; }; // 복사로 캡처
	func6();
	auto func7 = [&iExam1] { std::cout << "iExam1 : " << iExam1 << std::endl; }; // 참조로 캡처
	func7();

	int iTotal = 0;
	auto func8 = [&] { iTotal++; std::cout << "iTotal : " << iTotal << std::endl; };
	func8();

	auto func9 = [=] () mutable{ iTotal++; std::cout << "iTotal : " << iTotal << std::endl; };
	func9();

	int iNum1 = 1, iNum2 = 2;
	auto func10 = [iNum1, &iNum2]() mutable { std::cout << "iNum1 : " << iNum1 << " iNum2 : " << iNum2 << std::endl; };
	func10();

	system("pause");
}

7. STL의 알고리즘에서의 람다 사용

상황 : 죽은 유저를 찾는 상황에서의 find_if 알고리즘 사용

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

class User
{
private :
	int m_Index;
	bool m_bDie;
public :
	User() : m_bDie(false) {}
	~User(){}

	void SetIndex( int Index ) { m_Index = Index; }
	int GetIndex() { return m_Index; }
	void SetDie() { m_bDie = true; }
	bool IsDie() { return m_bDie; }
};

void main()
{
	vector<User> Users;
	User User01;
	User01.SetIndex( 1 );

	User User02;
	User02.SetIndex( 2 );
	User02.SetDie();		// 죽은 유저

	User User03;
	User03.SetIndex( 3 );

	Users.push_back( User01 );
	Users.push_back( User02 );
	Users.push_back( User03 );

	vector<User>::iterator Iter = find_if( Users.begin() , Users.end(), [](User& tUser)->bool{ return tUser.IsDie(); } );
	
	cout << "Dead User Index : " << Iter->GetIndex() << endl;

	system("pause");
}

만약 람다를 사용하지 않으면 코드는 아래와 같이 만들어야함

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

class User
{
private :
	int m_Index;
	bool m_bDie;
public :
	User() : m_bDie(false) {}
	~User(){}

	void SetIndex( int Index ) { m_Index = Index; }
	int GetIndex() { return m_Index; }
	void SetDie() { m_bDie = true; }
	bool IsDie() { return m_bDie; }
};

struct FindDeadUser
{
	bool operator() ( User& tUser ) const
	{
		return tUser.IsDie();
	}
};

void main()
{
	vector<User> Users;
	User User01;
	User01.SetIndex( 1 );

	User User02;
	User02.SetIndex( 2 );
	User02.SetDie();		// 죽은 유저

	User User03;
	User03.SetIndex( 3 );

	Users.push_back( User01 );
	Users.push_back( User02 );
	Users.push_back( User03 );

	vector<User>::iterator Iter = find_if( Users.begin(), Users.end(), FindDeadUser() );
	
	cout << "Dead User Index : " << Iter->GetIndex() << endl;

	system("pause");
}