C++람다식 사용이유와 사용 방법
기본문법
[] () -> int {}
[] : 캡쳐블록 ( 사용시 외부변수를 캡쳐해 람다의 몸통에서 사용가능하다.)
() : 전달인자
-> 반환타입
{} : 함수 몸통
여기서 캡쳐블록 [] 와 전달인자. ()와 리턴 타입{}을 생략할 수 있다.
사용 이유
함수객체와 다르게 클래스 선언을 할 필요가 없다.
함수포인터와는 다르게 인라인화가 가능하다.
람다의 캡쳐부분
기본적으로 클래스의 멤버 변수를 람다의 바디 안에서 사용했다면, 이는 this->가 생략되어 있다.
& : 모든 외부변수 참조
&, initialize : 모든 외부변수 참조 및 초기화 가능
= : 모든 외부변수 복사 (
=, intiailize : 모든 외부변수 복사 및 초기화 가능
[localVar] : 특정한 변수를 값에 의한 캡쳐 -> 변경해도 람다식 밖에서 그 변경이 적용되지 않는다.
[&localVar] : 특정한 변수를 레퍼런스에 의한 캡쳐 -> 변경하면 람다식 밖에서도 그대로 적용된다.
capture로 =를 가져왔을 경우 const 로 읽기만 가능하다.
안에서 값변경 불가능 하다.
int x = 10;
double y = 5.4;
string s = "hwan";
auto l1 = [=](){
int cp_x = x;
double cp_y = y;
string cp_s = s;
return x + y;
}();
auto l2 = [=, x = x + 1](){
cout << "l2함수의 x의 주소 값 : " << &x << endl;
return x * x;
};
cout << "l1 = " << l1 << endl;
cout << "l2 = " << l2() << endl;
cout << "l2 = " << l2() << endl;
cout << "x = " << x << endl;
cout << "main함수의 x 주소 값 : " << &x << endl;
15.4, 121, 121, 10(x값은 그대로다.) l2의 x랑 외부변수 x랑은 다른 값이다. (같은이름이면 외부변수는 사용되지 않는다.)
따라서 캡쳐안에서 연산을 할 수 있는 이유이다.
람다의 특징
1. 람다에서 선언된 변수는 프로그램이 끝날떄 까지 변수가 사라지지 않는다.
2. 외부변수와 같은 변수 이름으로 초기화를 한다면 해당 외부변수는 사용되지 않는다.
3. capture 블록에서 초기화된 변수는 const 로 선언이 된다. 초기화된 변수는 변경이 불가능하다.
4. capture로 =를 가져왔을 경우 const 로 읽기만 가능하다.
5. 람다는 이름은 없지만 고유한 객체이다. ( 검파일과정에서 람다라는 객체를 생성한다.)
6. 인라인화가 가능하다는게 람다의 가장큰 장점이다.
#include <iostream>
using namespace std;
int main() {
int x = 10;
auto l1 = [&](){
x = 5;
return x;
};
auto l2 = [&, x = x + 100](){
return x;
};
cout << l1() << endl;
cout << "main x : " << x << endl;;
cout << l2() << endl;
cout << "main x : " << x << endl;;
return 0;
}
결과는 5, 5, 110, 5 가 나온다.
이유는 capture안의 &의 특성이다.
ㅣ1에서 [&]이기 때문에 안에서 x의 값이 5로 바뀌었다 .
따라서 후에 출력되는 x는 5값을 가진다.
l2에서 capture안의 x = x + 100 의x는 외부변수 x랑은 다른값이다.
따라서 변하지 않는다.
그래서 l2를 빠져나와도 x값은 그대로 5가 된다.
그런데 여기서 'x = x + 100' 는 105가 되어야 할거같은데 110이 된다.
왜그럴까?
람다는 기본적으로 cosnt 로 변수값을 읽어오는 곳이 있다.
바로 코드블럭 맨뒤의 소괄후 () 부분이다.
ㅣ2 = [&, x = x + 10] () {...} ()
보라색 부분이 const 로 정의된 부분인다.
const 러 선언된 변수는 컴파일 후 메모리로 올라 가게 되는데 이때 data영역으로 들어가게 된다.
따라서 const 로 선언된 변수는 실행중 바뀔 수 없다.
함수 몸통뒤에 소괄호 () 부분은 람다의 실행을 의미함과 동시에 매개변수를 전달하는 역할을 한다.
결과 값이 110이 나온 이유는 람다 함수가 정의 될 때 이미 const 선언에 의해 미리 x값을 갖고 시작하기 때문입니다.
실질적으로 x값이 바뀌는 부분은 16행인 l1() 함수를 호출할 때 이고,
다음 l2()를 호출해도 초기 값은 실행하기에 앞서 미리 정의되었음으로 변경된 'x = 5' 가 아닌, 초기 값 'x = 10'을 가져오게 됩니다.
람다를 활용하면 함수객체를 간편하게 만들 수 있다.
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;
}
}
람다 자체가 함수처럼 자유롭게 사용할 수 있기 때문에 인자로 레퍼런스들도 전달이 가능하다.
예제1)
int total_elements = 1;
vector<int> cardinal;
cardinal.push_back(1);
cardinal.push_back(2);
cardinal.push_back(4);
cardinal.push_back(8);
for_each(cardinal.begin(), cardinal.end(), [&](int i) { total_elements *= i; });
cout << "total elements : " << total_elements << endl;
모든 원소가 곱해져서 나온다. 64가 나온다.
&이므로 total_elements를 캡쳐 할 수있다. 값을 바꿀수 있다는 의미이다.
예제2)
template <typename T>
void fill(vector<int>& v, T done) {
int i = 0;
while (!done()) {
v.push_back(i++);
}
}
vector<int> stuff;
fill(stuff, [&]() -> bool { return stuff.size() >= 8; });
for_each(stuff.begin(), stuff.end(), [](int i) { cout << i << " "; });
'프로그래밍 > C++' 카테고리의 다른 글
[C++]String 클래스 멤버함수 간단 요약 (0) | 2022.01.05 |
---|---|
[c++]이중포인터 (0) | 2021.10.04 |
[c++] 구조체, 구조체 배열, 구조체 포인터, 패딩바이트, typedef (0) | 2021.09.30 |
[C++] 2차원 배열, 배열 포인터, 배열 초기화, 배열 매개변수 (0) | 2021.09.24 |
[C++] 1차원 배열, 배열 포인터, 배열 초기화, 배열 매개변수 (0) | 2021.09.24 |