Post List

2014년 12월 22일 월요일

Lambda

Lambda (Visual Studio 2010부터 사용 가능. function pointer로 변환은 2012부터 가능)

'Lambda 함수' 또는 '무명 함수' (익명 함수, anonymous function) 으로 불린다. C#에서는 2008부터 가능했는데, C++은 2010부터 지원했을 헀고, Java는 8 버전 (2014) 이후에서야 지원을 하게 되었다.

 Lambda 함수 사용으로 C++의 표현력이 증가되며, STL을 좀 더 편리하게 사용할 수 있게 되있다. STL에서 많은 함수들은 function pointer를 인자로 받아서 실행되는 것이 많다.  (대표적인 예로 sort(),remove_if(), for_each() 등...) 특히 algorithm의 함수들은 대부분 다 그렇고, for_each 또한 그렇기 때문에 그게 불편해서도 for_each를 잘 사용하지 않았다. 안그래도 함수명을 지울 때 그 의미를 이해하기 쉽게 해야하며  다른 함수와 겹치지 않게 정의하는게 여간 골치아픈 일이 아닌데 다른 함수 내에서 사용하는 서브함수명을 또 지을려니 이것도 보통 일이 아니며, 현재 작성 중인 함수와는 또 다른 곳에 함수를 정의 하려니 소스 상에서 왔다 갔다하는 것도 짜증나는 일이다. Lambda를 사용함으로 위의 2가지 문제가 깔끔하게 해결이 가능하다. Lambda를 단순히 이름 없이 즉석해서 함수를 정의하고 사용하는 것만으로도 큰 의미가 있다.

 하지만 이것이 Lambda의 전부 다가 아니다. Lambda는 compile time에서 inline처럼 처리가 가능하므로 일반 function pointer 보다 훨씬 더 프로그램 성능을 향상 시켰다. function pointer 대신 function object를 사용했을 경우 성능이 향상되는데, 그 function object와 비슷한 성능을 성을을 lambda는 보장한다.

 그리고 Visual Studio 2010까지는 function pointer가 들어갈 곳에 lambda를 사용 할 수는 있었지만, 2012부터는 Lambda를 function pointer로 완벽한 변환이 가능하다. 단 stateless lambda에 한해서만 가능한데, 그건 생각해보면 당연한 말이다. 그로 인하여 Windows Message Handler, Callback function, Thread, Timer 등 함수를 인자로 받는 모든 곳에 사용이 가능해졌다.

- Lambda 문법

1. [ ] : lambda-introducer : 캡처절. 본문 바깥쪽 변수에 대한 Access 설정
2. ( ) : lambda-declarator : 함수의 매개 변수 목록
3. mutable : 캡처절에서 복사한 변수에 대하여 값 변경 여부
4. exception-specification : 함수의 예외 사양
5. trailing-return-type : 함수의 반환 형식
6. compound-statement : 함수의 본문



- capture 절

 lambda를 정의한 scope 내의 변수를 lambda 함수 내에서 사용 할 수 있도록 capture 할 수 있다.

* [ ] 빈칸으로 두면 아무 것도 capture 하지 않는 stateless lambda로 사용한다는 뜻이다. stateless lambda 만 function pointer로 변환이 가능하다.

* 모든 변수를 참조로 capture 할 때는 [&], 특정 변수만 참조로 capture 할 때는 [&변수] 로 선언한다.

* 모든 변수를 복사로 capture 할 때는 [=], 특정 변수만 복사로 capture 할 때는 [변수] 로 선언한다. 복사로 capture 한 변수는 lambda 내에서 변경이 불가능하다. 단 mutable 을 선언한 경우는 변경이 가능하나, lambda 함수 내에서만 그 값이 유효하다.


int
a1, a2, a3, a4, a5;auto f1 = [&, a1, a2]{};      // default 모든 변수 참조 / a1, a2 복사auto f2 = [=, &a1, &a2]{};    // default 모든 변수 복사 / a1, a2 참조auto f3 = [a1, a1] {};        // error : 'a1' is already part of the lambda capture listauto f4 = [&, &a1] {};        // error : '&a1' is not allowed when default capture mode is by-referenceauto f5 = [=, a1] {};         // error : '&a1' is required when the default capture mode is by copy
a1 = 1;
a2 = 2;[=]() { a1 = 2;  };       // error : 'a1' : a by copy capture cannot be modified in a non-mutable lambda[=, &a2]() mutable { a1 = 2; a2 = 5;  }; // OKstd::cout << a1 << _T(" , ") << a2 << std::endl; // a1 = 1, a2 = 5
 

- Lambda를 변수에 대입, 인자로 사용


template
<typename Func>
void Test(Func func)
{
        func();
}auto func = [] { std::cout << "Welcome to the Lambda World !" << std::endl;  };func();Test(func);
 

- Lambda 에서 인자 사용 및 반환


auto
func = [](int n) -> int { std::cout << n << std::endl;
                               return n; };int a = func(100);
 

- STL 에서 Lambda 사용 예제


std::
vector<int> v { 10, 20, 30, 40, 50 };std::for_each(v.begin(), v.end(), [](int i) { std::cout << i << std::endl; } );std::sort(v.begin(), v.end(), [](int l, int r) { return l > r; } );std::remove_if(v.begin(), v.end(), [](int i) { return i > 30 ? false : true; } );
 

- Generic Lambda 및 function pointer 로 형변환


auto
z = [](const auto x, auto y) { return x + y; }(3.14f, 7.56f);size_t s = [](auto&&... args) { return sizeof...(args); }(1, 2, 'a', _T("AAA"), nullptr);auto plus = [](auto a, auto b) { return a + b; };int i = plus(3, 4);double d = plus(3.14, 1.78);

int (*fp)(int, int) = plus;double (*fpd)(double, double) = plus;

- Message Handler 에 사용한 예제


WNDCLASS
wc = {};
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hInstance = module;
wc.lpszClassName = _T("window");
wc.lpfnWndProc = [](HWND window, UINT message, WPARAM wparam, LPARAM lparam) -> LRESULT
{
for (auto h = s_handlers; h != s_handlers + _countof(s_handlers); h++)
if (message == h->message)
return h->handler(window, wparam, lparam);
return DefWindowProc(window, message, wparam, lparam);
};
 

- Thread에 사용한 예제


auto
thread = CreateThread(nullptr, 0, [](LPVOID p) -> DWORD
{
ASSERT(p);
if (p != nullptr)
{
CCallbackDlg* pDlg = reinterpret_cast<CCallbackDlg*>(p);
if (pDlg)
{ ...  }
}
});
 



댓글 없음:

댓글 쓰기