람다

200원짜리개발자·2023년 8월 21일
0

C++

목록 보기
32/39
post-thumbnail

람다가 있기전에도 함수자(function)를 사용해서 만들 수 있긴 하다.
하지만 람다식을 배우게 된다면 더욱 더 편리하게 작업 할 수 있다!

그래서 중요하다!!

우리가 함수자나 함수 포인터를 실전에서 언제 사용하냐를 보면 predicate를 넣어줄 때나 인벤토리를 만들 때 사용한다.

인벤토리를 만들게 된다면 아래처럼 구현하게 될 것이다.

enum class ItemType
{
    None,
    Armor,
    Weapon,
    Jewelry,
    Consumable
};

enum class Rarity
{
    Common,
    Rare,
    Unique
};

class Item
{
public:
    Item() {}
    Item(int itemId, Rarity rarity, ItemType type) : _itemId(itemId), _rarity(rarity), _type(type) {}

public:
    int _itemid = 0;
    Rarity _rarity = Rarity::Common;
    ItemType _type = 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));
}

함수자, 함수포인터가 도움이 되었던 부분은 우리가 유니크인 아이템을 찾고싶다! 라고 하면 일일이 순회하는 코드를 만들면 코드가 방대해질 것이다.

만약 find_if를 사용한다면

struct IsUniqueItem
{
	bool operator()(Item& item)
    {
    	return item._rarity == Rarity::Unique;
    }
};

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

이런식으로 사용할 수 있었다.

하지만 매번 struct를 만드는 것은 너무 지저분하다.
그래서 오늘 배우는 람다식을 이용할 것이다.

람다

람다는 익명함수뿐만 아니라 함수 객체에 더 가깝다고 볼 수 있다. (상태까지 저장이 가능하다고 한다)

문법
일단 람다를 사용할 때
[](){}를 적고 시작한다.

우리가 함수를 만들 때 처럼 ()부분은 Input을 넣어주고 {}부분은 구현을 해주면 된다.

만약 우리가 위에 IsUniqueItem을 만들고 싶다면
(){}이 부분은 정해져 있을 것이다. 그리고 람다라는 이유로 []를 붙여주면 된다.

[](Item& item)
{
	return item._rarity == Rarity::Unique;
}

그래서 사용할 때는

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

이런식으로 사용해주면 된다.

객체를 만들어서 사용하고 싶다면

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

이런식으로도 사용할 수 있다.

단발성을 함수를 사용할 때 아주 유용하다.

그리고 우리가 여기서 위처럼만 사용을 한다면 익명 함수라고 부른다. (일회성 함수)
또 Input은 ()에 받는데 return type은 어떻게 판단하는 걸까? 이 생각을 할 수 있는데 auto와 template와 비슷하게 추론을 하는 것이다.

만약에 return type을 지정하고 싶다면

[](Item& item) -> int { return item._rarity == Rarity::Unique; };

이런식으로 사용해주면 된다. (넣지 않으면 자동 추론)

여기까지가 익명함수에 관한 내용이다.

우리가 함수 포인터를 사용할 때 상태를 가질 수 없다는 단점이 존재하였다.

만약 특정 아이템을 찾고 싶다! 라고 하면

struct IsWantedItem
{
	bool operator()(Item& item)
    {
    	return item._itemId == wantedId;
    }
    
    int wantedId = 0;
}

IsWandtedItem isWantedItem;
isWantedItem.wantedId = 2;

이렇게 상태를 가지고 객체처럼 만들어서 사용할 수 있었던 것이 함수 객체의 장점이 였는데

함수 객체의 기능까지 람다가 가지고 있다.

int wantedId = 2;

[&wantedId](Item& item) 
{
	return item._itemId == wantedId;
};

[]캡쳐 기능을 사용여 저장할 수 있다.
캡쳐 기능을 사용하여 wantedId라는 것을 받을 수 있다.

기본 캡쳐 모드
= 복사
& 참조

[\=] 모든 데이터 복사 방식
[\&] 모든 데이터 참조 방식

복사방식으로 하면 처음값이 계속 똑같이 유지될 것이고
참조방시으로 하면 주소값을 받아서 값을 추출할 것이다.

단일 변수마다 캡쳐 모드도 할 수 있다.

stl 알고리즘 시리즈와 궁합이 잘맞는다.

주의점

람다가 다 좋긴하지만 잘못사용하게 된다면 피를 볼 수 있다.

int wantedId = 2;

[&wantedId](Item& item) 
{
	return item._itemId == wantedId;
};

이런식으로 참조해서 가지고 있을 때 주소값이 날라가 버린다면 날라간 주소를 참조해 문제가 될 수도 있다.

마무리

중요한 것!!
람다 문법에 익숙해지기!
[](){}이거 3가지를 적고
일반 함수를 만들듯이
\캡쳐모드 -> return type지정(안해도 됨)
{
내용물(구현부)
}
functor처럼 상태를 가지고 싶을때만 캡쳐모드를 사용하면 된다.

그래서 우리가 앞으로 std::find같은 시리즈를 이용할 때 predicate를 구현할 때 도움이 많이 될 것이다. (어차피 predicate는 거의 한 번만 만들어 준다)

std::find_if(v.begin(), v.end(), [](Item& item){ return item._itemId == 2; }

람다는 처음에는 어렵지만 하다보면 편해져서 많이 사용하게 될 것이다!

profile
고3, 프론트엔드

0개의 댓글