Modern C++ - 람다(Lambda)

이승덱·2021년 7월 21일

CPP

목록 보기
69/70
#include <iostream>

#include <vector>

#include <list>

#include <deque>

#include <map>

#include <set>

#include<algorithm>

using namespace std;

// Modern C++ (C++11 부터 나온 아이들)

// 람다(lambda)

// 함수 객체를 빠르게 만드는 문법

enum class Rarity {

 Common,

 Rare,

 Unique,

};

enum class ItemType {

 None,

 Armor,

 Weapon,

 Jewelry,

 Consumale,

};

class Item {

public:

 Item(){}

 Item(int itemId, Rarity rarity,ItemType itemType):_itemId(itemId),_rarity(rarity),_itemType(itemType){}

public:

 int _itemId = 0;

 Rarity _rarity = Rarity::Common;

 ItemType _itemType=ItemType::None;

};

int main()

{

 vector<Item> v;

 v.push_back(Item(1, Rarity::Common, ItemType::Weapon));

 v.push_back(Item(2, Rarity::Common, ItemType::Armor));

 v.push_back(Item(3, Rarity::Rare, ItemType::Jewelry));

 v.push_back(Item(4, Rarity::Unique, ItemType::Weapon));

 // 람다 = 함수 객체를 손쉽게 만드는 문법

 // 람다 자체로 C++에 '새로운' 기능이 들어간 것은 아니다

 {

 struct IsUniqueItem

 {

 bool operator()(Item& item) {

 return item._rarity == Rarity::Unique;

 }

 };

 auto findIt = std::find_if(v.begin(), v.end(),IsUniqueItem());

 if (findIt != v.end()) {

 cout << "아이템ID: " << findIt->_itemId << endl;

 }

 else {

 cout << "못찾음" << endl;

 }

 // 람다를 활용하면 함수객체를 간편하게 만들 수 있다.

 // 람다의 시작 (익명 함수)

 // 반환 형식을 지정하지 않음 ( []() -> int {}; 식으로 반환 형식을 지정할 수 있다)

 // 클로저 (closure) = 람다에 의해 만들어진 실행시점 객체

 auto IsUniqueLambda = [](Item& item) {

 return item._rarity == Rarity::Unique;

 };

 auto findIt2 = std::find_if(v.begin(), v.end(), IsUniqueLambda);

 auto findIt3 = std::find_if(v.begin(), v.end(), [](Item& item) { return item._rarity == Rarity::Unique;});

 if (findIt3 != v.end()) {

 cout << "아이템ID: " << findIt3->_itemId << endl;

 }

 else {

 cout << "못찾음" << endl;

 }

 }

 // 캡처를 사용한 람다

 {

 struct FindItemByItemId

 {

 FindItemByItemId(int itemId):_itemId(itemId) {

 }

 bool operator()(Item& item) {

 return item._itemId == _itemId;

 }

 int _itemId;

 };

 int itemId = 4;

 auto findIt = std::find_if(v.begin(), v.end(), FindItemByItemId(itemId));

 if (findIt != v.end()) {

 cout << "아이템ID: " << findIt->_itemId << endl;

 }

 else {

 cout << "못찾음" << endl;

 }

 

 // 클로저 (closure) = 람다에 의해 만들어진 실행시점 객체

 // [ ] 캡처(capture) : 함수 객체 내부에 변수를 저장하는 개념과 유사

 // 사진을 찍듯.. 일종의 스냅샷을 찍는다고 이해

 // 기본 캠처 모드 : 값(복사) 방식(=) 참조 방식(&)

 // 말 그대로 복사 방식은 값을 복사하는 방식

 // 참조 방식은 데이터를 참조하기 때문에 참조한 값이 바뀌면 연산에 사용하는 데이터 또한 바뀜

 auto FindItemByItemIdByLambda = [/*=*/&](Item& item) {

 return item._itemId == itemId;

 };

 itemId = 2;

 auto findIt2 = std::find_if(v.begin(), v.end(), FindItemByItemIdByLambda);

 auto findIt3 = std::find_if(v.begin(), v.end(), [&](Item& item) {

 return item._itemId == itemId;

 });

 if (findIt3 != v.end()) {

 cout << "아이템ID: " << findIt3->_itemId << endl;

 }

 else {

 cout << "못찾음" << endl;

 }

 }

 {

 struct FindItem

 {

 FindItem(int itemId,Rarity rarity, ItemType type) :_itemId(itemId),_rarity(rarity),_itemType(type) {

 }

 bool operator()(Item& item) {

 return item._itemId == _itemId&&item._rarity==_rarity&&item._itemType==_itemType;

 }

 int _itemId;

 Rarity _rarity;

 ItemType _itemType;

 };

 int itemId = 4;

 Rarity rarity=Rarity::Unique;

 ItemType itemType=ItemType::Weapon;

 auto findIt = std::find_if(v.begin(), v.end(), FindItem(itemId, Rarity::Unique, ItemType::Weapon));

 if (findIt != v.end()) {

 cout << "아이템ID: " << findIt->_itemId << endl;

 }

 else {

 cout << "못찾음" << endl;

 }

 auto FindItemByLambda = [=](Item& item) {

 return item._itemId == itemId;

 };

 auto findIt2 = std::find_if(v.begin(), v.end(), FindItemByLambda);

 // 람다 선언 시 참조 방식인지 값 방식인지 명시하여 사용하는 것을 권장함

 // 모두 참조, 모두 값 방식으로 통일하는 것은 권장하지 않음

 auto findIt3 = std::find_if(v.begin(), v.end(), [&itemId,rarity,itemType](Item& item) {

 return item._itemId == itemId && item._rarity == rarity && item._itemType == itemType;

 });

 if (findIt3 != v.end()) {

 cout << "아이템ID: " << findIt3->_itemId << endl;

 }

 else {

 cout << "못찾음" << endl;

 }

 }

 // 구조체(클래스)를 만들어 사용해야하는 함수 객체를

 // 람다를 사용하면 매우 편리하게 사용이 가능

 {

 class Knight {

 public:

 auto ResetHpJob() {

 // 클래스 객체를 캡처하여 사용하는 경우

 // this를 명시적으로 입력해 가독성을 향상시켜주는 편이 좋다

 auto f = [this]() {

 this->_hp = 200;

 };

 return f;

 }

 public:

 int _hp = 100;

 };

 // 위 방식을 함수 객체로 표현

 class Functor {

 public:

 Functor(Knight* k) :_knight(k) {

 }

 void operator()() {

 _knight->_hp = 200;

 }

 public:

 Knight* _knight;

 };

 Knight* k = new Knight();

 auto job = k->ResetHpJob();

 delete k; // job이 가진 knight는 소멸된 상태

 job; //메모리 오염이 일어남 *주의*

 }

 // 결론: Functor을 만들어 사용하는 것 보다 Lambda를 이용하는 편이 훨씬 편리하고 가독성이 좋다

 // [캡처] (인자값) {구현부};

 // 캡처는 값 복사와 참조 방식이 있는데 값 복사는 말그대로 값만 사용하는 것이고 참조 방식은 데이터의 주소를 참조하여 사용

 // 캡처를 사용할 경우 각각의 인자가 값 복사 방식인지 참조 방식인지 명시해주는 것이 좋다

 return 0;

}
profile
공부 기록용 블로그입니다

0개의 댓글