lambda가 왜 필요하냐면,
함수 포인터는 함수이기 때문에 상태를 가질 수 없다. 반복문 같은데 인자로 함수를 넘겨야 하는 상황에서 그 함수가 상태를 가져야 하는 경우, 함수 포인터를 사용할 수 없다. 따라서 상태를 가지는 함수를 만들기 위해서 함수 객체라는 트릭을 사용해야 했다. 함수 객체는 객체인데, operator()
를 오버로드해서 함수처럼 호출할 수 있는 객체를 의미한다. Function object는 보통 Functor라고 부른다.
1
2
3
4
5
| struct Functor {
void operator()() {
std::cout << "Functor" << std::endl;
}
};
|
이렇게 별도의 Functor를 정의해주는 것이 번거롭고 지저분하다.
그래서 상태를 가지는 함수를 표현하기 위해 lambda를 사용한다.
1
2
| [captures](parameters) { body }
// mutable, exception, trailing-return-type 지정도 가능하나 선택 사항이다.
|
캡쳐할 변수를 []
에 지정하면 된다. 기본은 값 캡쳐이며 &var
를 지정하면 참조 캡쳐다. 캡쳐할 변수를 명시적으로 지정하지 않아도, 바디에서 쓰이는 변수가 자동으로 캡쳐되도록 할 수 있다.
[&]
바디에서 쓰이는 모든 변수, 상수를 참조로 캡쳐하고 현재 객체 참조 캡쳐.[=]
바디에서 쓰이는 모든 변수, 상수를 값으로 캡쳐하고 현재 객체 참조 캡쳐.[]
아무것도 캡처하지 않음.[this]
현재 객체 참조 캡쳐 * 참고. std::bind()
에서 참조 캡쳐(?)처럼 사용하려면 cref(variable)
를 사용한다. 보통 bind 보다는 그냥 람다 쓰는게 좋다.
다음과 같이 람다 내부에서 외부에 있는 변수의 값을 수정할 수 있다.
1
2
3
4
| int i = 0;
auto f = [&i](std::string name) {std::cout << ++i << name << '\n'; };
f("umbum");
std::cout << i << '\n';
|
그런데, 람다 내부에서 외부에 있는 변수를 참조가 아니라 값으로 받아와 수정하고 싶다면 mutable
을 붙여주어야 한다.
1
2
3
4
| int i = 0;
auto f = [i](std::string name) mutable {std::cout << ++i << name << '\n'; };
f("umbum");
std::cout << i << '\n';
|
this
를 넣으면 현재 객체를 참조 캡쳐할 수 있다. lambda는 friend 함수로, private
로 지정된 멤버에도 접근 가능하다.
1
| [this]() { std::cout << "cls name : " << name << std::endl; }();
|
bind와 bind1st는 호환이 되긴 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| #include <functional>
#include <boost/iterator/transform\_iterator.hpp>
void bind\_test() {
int x[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
const int N = sizeof(x) / sizeof(int);
typedef std::\_Binder<std::\_Unforced, std::multiplies<int>, int, const std::\_Ph<1> & > Function;
typedef boost::transform\_iterator<Function, int\*> doubling\_iterator;
using namespace std::placeholders;
auto bind\_f = std::bind(std::multiplies<int>(), 2, \_1);
doubling\_iterator i(x, bind\_f);
doubling\_iterator i\_end(x + N, bind\_f);
std::cout << "multiplying the array by 2:" << std::endl;
while (i != i\_end)
std::cout << \*i++ << " ";
std::cout << std::endl;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| void bind1st\_test() {
int x[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
const int N = sizeof(x) / sizeof(int);
typedef std::binder1st< std::multiplies<int> > Function;
typedef boost::transform\_iterator<Function, int\*> doubling\_iterator;
auto bind1st\_f = std::bind1st(std::multiplies<int>(), 2);
doubling\_iterator i(x, bind1st\_f);
doubling\_iterator i\_end(x + N, bind1st\_f);
std::cout << "multiplying the array by 2:" << std::endl;
while (i != i\_end)
std::cout << \*i++ << " ";
std::cout << std::endl;
}
|
근데 bind1st(), binder1st
같은 경우 C++11에서 deprecated 되었고, 그냥 bind()
는 람다의 한계(템플릿으로 여러 타입 받기, move capture)로 아직까지는 사용하나 이런 케이스가 아니라면 그냥 람다를 사용하는 편이 좋다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| void lambda\_test() {
int x[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
const int N = sizeof(x) / sizeof(int);
typedef boost::transform\_iterator<std::function<int(int)>, int\*> doubling\_iterator;
std::function<int(int)> doubling\_lambda = [](int x) { return x \* 2; }; // or just auto.
doubling\_iterator i(x, doubling\_lambda);
doubling\_iterator i\_end(x + N, doubling\_lambda);
std::cout << "multiplying the array by 2:" << std::endl;
while (i != i\_end)
std::cout << \*i++ << " ";
std::cout << std::endl;
}
|