[람다]

 

C++에서 함수를 선언할 때는 보통 선언과 구현을 실시한다. 하지만 람다를 사용할 경우 선언과 동시에 바로 함수처럼 활용할 수 있다.

[캡처](매개변수){함수바디}(호출인자) // 형태

[](int _a, int _b){return a + b};          // 함수를 만들기만 한 것
[](int _a, int _b){return a + b}(10, 20);  // 이건 함수를 만들고 바로 쓴 것

 

캡처를 선언하고, 매개 변수를 설정한 뒤 구현부를 등록해주고 필요 시 매개변수를 넣어주면 된다. 매개 변수와 호출인자는 사용하지 않으면 보통 생략할 수 있다.

 

 

[사용처]

 

여러 곳에서 무궁무진하게 활용 가능하겠지만, 작성자의 경우에는 Inline으로 처리될 수 있을 정도의 짧은 함수이면서 굳이 재활용하지 않는 함수일 경우 사용하곤 한다. 일반적인 sort의 경우 오름차순 정렬이 실시되지만, 특정 로직을 추가 실행하거나 할 경우에는 함수 포인터로 원하는 로직이 작성된 함수를 전달해주면 된다. 이때 람다를 사용할 수 있다.

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main()
{
	vector<int> Arr = { 8, 4, 10, 2, 7, 9, 1};
	sort(Arr.begin(), Arr.end(), [](int _Left, int _Right) { return _Left > _Right; });
	
	for (int Value : Arr) cout << Value << endl;

	return 0;
}

 

 

[람다 캡쳐]

 

람다 사용의 주의점은 해당 구현부가 현재의 스택에서 분리된다는 점이다. 예를들어, 아래와 같이 람다를 선언하면 람다의 함수 바디는 main 함수의 스택 영역과 분리된 영역으로 존재하게 된다.

int main()
{
    int Value = 0;
    [](int _value)
    {
       int Value = 0;
    };
}

 

하지만 이를 위해 람다 캡처를 지원해주는데, 람다 캡처에 특정 연산자를 활용하면 스택에 존재하는 값을 활용할 수 있게 된다. 캡처의 종류는 네 가지가 있다.

 

- =

- &

- 단일캡처

- this

 

 

1. =캡처

해당 캡처는 모든 외부 변수를 복사해서 람다 함수 내부에서 사용한다는 뜻이 된다.

int main()
{
	int a = 10;
	int* IntPtr = &a;

	std::cout << "IntPtr : " << IntPtr << std::endl;

	std::function<void()> Ptr = [=]()
		{
			int* RamPtr = nullptr;
			RamPtr = IntPtr;

			std::cout << "IntPtr : " << IntPtr << std::endl;
			std::cout << "RamPtr : " << RamPtr << std::endl;
		};

	Ptr();
}

 

 

하지만 해당 캡처 방식은 단순하게 외부에 존재하는 변수에 대한 대입이 이뤄지지 않고 에러가 발생하는데, 아래의 테스트를 해보면 이유를 확인해볼 수 있다.

// 람다 구현부에서 외부 변수 int a;에 대해 a = 20; 수행이 안되는 이유
int main()
{
	int a = 20;
	__int64 aAddRess = (__int64)&a;

	std::cout << "a : " << a << std::endl;
	std::cout << "aAddRess : " << aAddRess << std::endl;

	std::function<void()> Ptr = [=]()
		{
			__int64 aAddRess = (__int64)&a;

			std::cout << "a : " << a << std::endl;
			std::cout << "aAddRess : " << aAddRess << std::endl;
		};

	Ptr();
}

 

메모리의 주소가 차이난다는 것을 확인할 수 있는데, 이것은 [=캡처]가 단순히 호출한 영역의 메모리를 복사해서 들고있기 때문에 발생하는 것이다.

 

 

2. &캡처

위 현상을 해결하기 위해 [&캡처]가 있다. 이전과 동일하게 테스트해보면 외부 변수와 람다 구현부 변수의 주소값이 동일한 것을 확인할 수 있다.

int main()
{
	int a = 20;
	__int64 aAddRess = (__int64)&a;

	std::cout << "a : " << a << std::endl;
	std::cout << "aAddRess : " << aAddRess << std::endl;

	std::function<void()> Ptr = [&]()
		{
			int Test = 100;
			a = Test;
			__int64 aAddRess = (__int64)&a;

			std::cout << "a : " << a << std::endl;
			std::cout << "aAddRess : " << aAddRess << std::endl;
		};

	Ptr();
}

 

해당 캡처 방식은 호출한 영역의 메모리를 참조해서 활용하는 방식이다. 물론 이 방식은 스택 영역에서 활용할 경우 외부 변수가 파괴될 염려가 있어 사용에 주의해야 한다.

 

 

3. 단일캡처

영역의 메모리 전체를 복사하는 방식이 아니라, 단일 변수에 대해서도 복사가 가능하다.

int main()
{
	int a = 20;
	int b = 30;
	__int64 aAddRess = (__int64)&a;
	__int64 bAddRess = (__int64)&b;

	std::cout << "a : " << a << std::endl;
	std::cout << "b : " << b << std::endl;
	std::cout << "aAddRess : " << aAddRess << std::endl;
	std::cout << "bAddRess : " << bAddRess << std::endl;

	std::function<void()> Ptr = [&a, b]()
		{
			int Test = 100;
			a = Test;
			Test = b;
			__int64 aAddRess = (__int64)&a;
			__int64 bAddRess = (__int64)&b;

			std::cout << "a : " << a << std::endl;
			std::cout << "b : " << b << std::endl;
			std::cout << "aAddRess : " << aAddRess << std::endl;
			std::cout << "bAddRess : " << bAddRess << std::endl;
		};

	Ptr();
}

 

 

4. this캡처

물론 클래스 멤버변수까지 캡처하는 방식도 가능하다. 이때는 호출하는 대상의 this를 캡처에 전달하면 된다. [&캡처]와 유사한 방식으로 동작한다.

class Test
{
public:
	void Call()
	{
		__int64 aAddRess = (__int64)&a;
		__int64 bAddRess = (__int64)&b;

		std::cout << "a : " << a << std::endl;
		std::cout << "b : " << b << std::endl;
		std::cout << "aAddRess : " << aAddRess << std::endl;
		std::cout << "bAddRess : " << bAddRess << std::endl;

		std::function<void()> ClassFunction = [this]()
			{
				int Test = 100;
				a = Test;
				Test = b;
				__int64 aAddRess = (__int64)&a;
				__int64 bAddRess = (__int64)&b;

				std::cout << "a : " << a << std::endl;
				std::cout << "b : " << b << std::endl;
				std::cout << "aAddRess : " << aAddRess << std::endl;
				std::cout << "bAddRess : " << bAddRess << std::endl;
			};

		ClassFunction();
	}

	int a = 0;
	int b = 0;
	__int64 aAddRess = 0;
	__int64 bAddRess = 0;
};

int main()
{
	Test Obj = Test();
	std::function<void()> Function = std::bind(&Test::Call, Obj);
	Function();
}

 

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

[C++] 클래스 template  (0) 2025.05.13
[C++] 함수 template  (0) 2025.05.13
[C++] 캐스팅(Casting)  (0) 2025.05.13
[C++] std::enable_shared_from_this와 shared_from_this()  (0) 2025.05.13
[C++] 스마트 포인터  (0) 2025.03.21

+ Recent posts