본문 바로가기

C++

c++] lambda 표현식을 차근차근 써보자(수정...)

(3) lambda 표현식


기본적인 틀 : [capture](params)->return_type{body}(params)

의미는 [ 이 람다표현식을 포함한 중괄호 내에서 끌어올 변수가 무엇인가요 ]( parameter들의 자료형은 무엇이고 얼마나 더 있나요 )->( 리턴타입은 무엇입니까 ){ capture한 변수를 갖고 무엇을 할 것인가요 }( 실제 파라미터들은 무엇입니까 )라는 의미가 된다. 차근차근 사용해보자.


"Hello lambda"

[](){ cout<<"Hello lambda"<<endl; }();


위에서 볼 수 있듯이 void형일 경우 "->"가 사라진 것을 볼 수 있다.(->를 사용하면 컴파일이 되지 않는다.)


값의 변화

int main(){

int a =1;

......

}


위의 main내부에 a의 값을 1증가 시키는 람다 표현식을 작성해보면

 표현식

 컴파일 결과

 설명

 [](){a++;}();

 X

 람다표현식이 a를 알 수 있는 방법이 없다. capture를 통해서 a의 존재를 알아야 하기 때문이다.

 [a](){a++;}();

 X

capture를 통해서 a를 어떻게 한다는 것은 알았는데 이 방법으로는 a는 immutable이 됩니다. 

 [&a](){a++;}();

 O

a를 참조하는 것이군요~. 인자로 아무것도 받지 않고 리턴타입은 void 입니다~ 

위의 결과를 통해 capture는 람다 표현식 외부 변수를 받아와서 body에서 써먹는 모양이구나 하는 것을 알 수있다. 그리고 '받아올 때 어떻게 받아올 것인지(복사와 참조)' , '복사해왔으면 그것을 수정할 수 있는지 없는지'를 결정해줘야 하는 것을 알 수있다.


다음 표는 위에서 컴파일 할 수 없었던 경우들을 대상으로 우리의 목표에 맞게 바꾼 표현식이다.


 수정후 표현식

설명 

 [](){a++;}();

int a = 1;

int main(){

 [](){a++;}();

}

 지역변수 int a를 전역변수로 바꿔준다.

 [a](){a++;}();

 a = [a]()->int{return a+1;}()

 a를 복사해오는 경우 a의 값을 변경해 줄수 없기 때문이다.

[a]()mutable{a++;}() 처럼 비록 mutable 지정자를 이용해 a 값을 증가시키더라도 외부 a의 값은 영향이 가지 않는다.


매번 캡쳐해줘야 해?

int main(){

int a = 0;

int b = 1;

int c = 2;

[&a,&b,&c](){a++;b++;c++;}();

}


위와 같이 a,b,c를 모두 참조하려다 보면 번거러워질 수도 있다. 이 때는 아래와 같이 capture안에 [&]를 써서 lambda 표현식을 포함한 중괄호 내부의 모든 변수를 참조 하도록 할 수 있다. 마찬가지로 모든 변수를 복사하고자 할 때는 [=]를 사용하면 된다.


int main(){

int a = 0;

int b = 1;

int c = 2;

//[&a,&b,&c](){a++;b++;c++;}();

[](){a++;b++;c++;}();

}


Class 안에서 쓸 때는?

Class안에서 lambda표현식을 Class 멤버로 가지려면 어떻게 해야 할까?


"a와 b를 증가시키는 lambda 표현식을 멤버로 가지는 클래스를 작성해보시오"


class Node{

int a;

int b;

Node():a(1),b(2){}

[&a](){a++;}();

[&b](){b++;}();

}


위와 같이 작성하면 클래스에서 람다 함수를 어떻게 호출해야 할까? Node node;  node.[&a]..... 이렇게? 당연하게도 위와 같은 표현은 컴파일이 되지 않는다. 


class 띠용{

3

};


이렇게 한다고 해서 띠용 클래스가 3을 멤버로 가지는가? 마찬가지로, lambda 표현식의 결과인 함수 객체를 담을 멤버 변수가 클래스에 필요하다.

이를 std::function을 이용하여 수정하면


class Node{
    int a;

int b;

Node():a(1),b(2){}

std::function<void()> a_plus_one = [&a](){a++;}; //body 다음에 나오는 ()가 사라졌으니 주의!!

std::function<void()> b_plus_one = [&b](){b++;};

}


하지만 이 역시도 컴파일 되지 않는다. 클래스 내부에서 람다 함수를 쓸때는 캡쳐에서 [this]를 써줘야 한다. 따라서

class Node{
    int a;

int b;

Node():a(1),b(2){}

std::function<void()> a_plus_one = [this](){a++;}; //auto a_plus_one = [this](){a++;}; 역시도 가능하다

std::function<void()> b_plus_one = [this](){b++;}; 

}


이를 테스트 해보면


Node node;
cout << "lambda 함수를 호출하기 전 a: " << node.a << endl;
node.a_plus_one();
cout << "lambda 함수를 호출한 후 a: " << node.a << endl; 
결과:


정상적으로 동작하는 것을 볼 수 있다. 다음은 함수 포인터, std::function와 함수 객체를 소개하도록 하겠다.




출처: https://tt91.tistory.com/11

  https://blog.koriel.kr/modern-cpp-lambdayi-teugjinggwa-sayongbeob/

https://m.blog.naver.com/PostView.nhn?blogId=gamejung13&logNo=220571625118&proxyReferer=https%3A%2F%2Fwww.google.com%2F

'C++' 카테고리의 다른 글

c++] 레퍼런스와 연산자 오버로딩(2)  (1) 2019.03.07
c++] 레퍼런스와 연산자 오버로딩(1)  (0) 2019.03.06