C++복습

C++복습) Modern C++/lambda

PJNull 2021. 12. 30.
728x90
반응형

lambda

lambda는 C++11에서 새로운 개념이 추가되어서 나온것이 아닌 함수객체를 빠르게 만드는 문법이다. 주로 1회성 함수객체를 만들때 사용한다. 그렇다면 왜 이러한 람다식이 필요로 하는것인가?

 

람다의 필요성

enum class ItemType
{
 None,
 Armor,
 Weapon,
 Jewel,
}
enum class Rarity
{
 Common,
 Rare,
 Unique,
};
class Item
{
 public:
  
  item(){}
  item(int id,Rarity rare,ItemType type):_itemid(id),_rare(rare),_type(type)
 public:
  int _itemid=0;
  Rarity _rare=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::Rare,ItemType::Armor));
 
 v.push_back(Item(3,Rarity::Unique,ItemType::Jewel));
}

아이템들은 특정 조건에 맞춰서 탐색/제어 해야 될 상황이 필요할 것이다.

{
struct IsUnique()
{
 bool operator()(Item& item)
 { 
   return item._rare==Rarity::Unique;
 }
}
 auto find = std::find_if(v.begin(),v.end(),IsUnique());
 if(find!=v.end())cout<<find->_itemid;
}

이런 식으로 그때그때 상황에 맞는 Functor를 제작해야한다. 위 코드와 같이 아이템이 별로 없을 때는 무리가 없지만, 아이템이 많이 짐에 따라서 조건이 세분화 되고 다양해지게 된다면 Functor의 수 또한 매우 늘어나게 될것이다. 그런데 이렇게 Functor를 매번 생성해서 넣어주게 된다면 가독성 문제 및 코드 반복이 발생 할 수도 있다. 이것을 어느정도 방지하는 것이 람다식의 의의이다.

 

람다표현식

람다는 익명 함수이며 표현식은 [캡처](인자){구현부}로 되어있다.

 

auto IsUniqueLambda=[](Item& item){return item._rare==Rarity::Unique;}//클로저
//[]()->int{}와 같이 ()->자료형{} 형태를 취하게 되면 return 값을 정해줄수있다.

 auto find = std::find_if(v.begin(),v.end(),IsUnique());
 
 auto find = std::find_if(v.begin(),v.end(),IsUniqueLambda);
 
 auto find = std::find_if(v.begin(),v.end(),[](Item& item){return item._rare==Rarity::Unique;});
//모두 같은 역할을 수행한다.

이 코드가 위에서 구현했던 Functor와 동일한 역할을 수행한다.이렇게 람다식에 의해 실행시점에서 만들어진 객체를 클로저라고 한다.  이러한 람다식은 Functor와 차별화된 기능이 하나 있다.

 

struct FindItemId()
{
 FindItemId(int itemid):_itemid(itemid){}
 bool operator()(Item& item)
 { 
   return item._itemid==itemid;
 }
 int _itemid;
}

int itemid=3;

auto findLambda=[=](Item& item){return item._itemid==itemid}
//[]()->int{}와 같이 ()->자료형{} 형태를 취하게 되면 return 값을 정해줄수있다.

 auto find = std::find_if(v.begin(),v.end(),FindItemId(itemid));
 
 auto find = std::find_if(v.begin(),v.end(),[](Item& item){findLambda});
 //동일한 기능을 수행한다.

Functor에서는 인자로 받아서 내부에 저장을 해야만 했지만, 람다에서는 캡처에 의해 외부의 인자를 가져다 사용할수 있다.

 

[](캡처)

함수 객체 내부에 변수를 저장하는 개념이다.

 

기본캡처모드

기본캡처모드에는 값/복사방식(=)과 참조방식(&)이 있다.

 

  • 값방식(=)
auto findLambda=[=](Item& item){return item._itemid==itemid}

캡처를 비워두게 되면 기본적으로 값 방식으로 동작하게된다. 외부의 값을 받아 오는 역할을 수행한다.

 

  • 참조방식(&)
auto findLambda=[&](Item& item){return item._itemid==itemid}

참조값을 저장하는 형태이며 원본을 참조하는 형태이기 때문에 기존의 참조형태랑 동일한 역할을 수행한다.

 

지정캡처모드

기본캡처모드에서는 값방식을 택하면 모든 인자가 값방식으로 되고,참조방식을 택하면 모든 인자가 참조 방식으로 되지만 지정 캡처모드에서는 각 인자의 방식을 지정할수 있으며 가독성과 같은 이유로 지정캡처모드를 권장한다.

 FindItem(int itemid,Rarity rare,itemType item):_itemid(itemid),_rare(rare),_type(item)_{}
 bool operator()(Item& item)
 { 
   return item._itemid==itemid && ;item._rare==_rare && item._type==_type;
 }
 int _itemid;
 Rarity _rare;
 itemType _type;

예를 들어 이런식으로 찾아야될 조건이 늘어났다고 가정하고 itemid는 값방식, rare와 type은 참조방식으로 하고 싶다고 한다면, 다음과 같이 작성하는 것이 지정캡처모드이다.

auto findLambda=[itemid,&](Item& item)
{
  return item._itemid==itemid && item._rare==_rare && item._type==_type;
}
auto findLambda=[=itemid,&rare,&type](Item& item)
{
  return item._itemid==itemid && item._rare==_rare && item._type==_type;
}
//두 코드 모두 똑같다

 

 

 

728x90
반응형

댓글