1. 람다(lambda)

함수 객체(함수처럼 작동하는 객체)를 빠르게 만드는 문법.

 

아이템 id, 희귀도, 타입을 받는 클래스를 만든다.

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;

};

그리고 새로운 벡터를 만들어서 4개의 아이템을 push_back 한다.

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));
}

이제 함수 객체(Functor ; 펑터)를 사용하여 특정 조건의 아이템을 찾아주는 기능을 구현해본다.

    {// 특정 조건에 따라서 아이템 찾기
    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 << findIt->_itemid;
    }
}

람다를 사용하여 위의 함수 객체를 간단하게 구현할 수 있다.

 

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

- 특징

(1) 컴파일러가 함수 객체를 적당한 이름으로 사용한다.

(2) 반환 형식을 컴파일러가 판단하여 적용한다.

(여기에서는 boolean으로 반환한다)

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

또는 펑터를 임시적으로 사용하겠다고 하면 findIt 함수 안에 그대로 쓸 수도 있다.

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

(3) 펑터의 데이터를 저장할 수 있다는 장점도 그대로 가지고 있다.

 

- 클로저(Closure)

람다에 의해 만들어짐 실행 시점의 객체

 

2. 캡쳐(Capture)

람다 표현식 내부에서 외부 변수를 사용할 수 있게 함.

 

- 기본 캡쳐 모드

(1) 값(복사) 방식

    int itemId = 3;
    
    auto findByItemIdLambda = [=](Item& item)
    {
        return item._itemid == itemId; // itemId = 3의 값을 사용
    };

(2) 참조 방식

    int itemId = 3;
    
    auto findByItemIdLambda = [&](Item& item)
    {
        return item._itemid == itemId; // itemId의 주소값을 사용
    };
    
   	itemId = 10;
    
    auto findIt = std::find_if(v.begin(), v.end(), findByItemIdLambda());
    // itemid = 10인 아이템을 찾음

 

- 혼합 캡쳐 ; 변수마다 캡처 모드를 다르게 지정하기

    auto findByItemIdLambda = [&itemId, rarity, type](Item& item)
    {
        return item._itemid == itemId && item._rarity == rarity && item._type == type;
    };

코드 가독성을 높이기 위해 혼합 캡쳐를 사용하는 것이 권장된다.

 

2. 람다 표현식을 사용할 때 발생할 수 있는 문제

    class Knight
    {
    public:
        auto ResetHpJob()
        {
            auto f = [this]()
            {
                this->_hp = 200;
            };
            return f;
         }

    public:
        int _hp = 100;
    };

이렇게 클래스의 멤버 함수를 만들고 이후에 객체를 생성한다.

    Knight* k = new Knight();
    auto job = k->ResetHpJob();
    delete k;
    job();

위에서 처럼 새롭게 동적 할당을 통해 Knight의 객체를 생성하고 내부 변수를 사용 후에 객체를 제거한 이후에

또 멤버 함수에 접근하게 되면 엉뚱한 메모리 영역을 수정하는 문제가 발생할 수 있다.

 

여기에서는 변수 f에 람다 표현식을 사용할 때 캡쳐 모드에 this 포인터를 명시해주어서 문제를 빠르게 해결할 수 있으나

그렇지 않으면 문제를 찾기 어려워진다.

'기초 C++ 스터디 > 모던 C++' 카테고리의 다른 글

10-10. 스마트 포인터  (0) 2023.06.26
10-8. 전달 참조 (forwarding reference)  (0) 2023.06.22
10-7. 오른값 참조(Rvalue Reference)  (0) 2023.06.22
10-6. override, final  (0) 2023.06.20
10-5. delete - 삭제된 함수  (0) 2023.06.20