C++에서 람다식을 std::function을 이용해 파라미터로 넘기면 시스템 내부적으로는 함수 객체(function object)를 생성하고, 이 함수 객체를 std::function 객체에 저장합니다.
람다식은 익명 함수 객체를 생성하는 것이며, 이 함수 객체는 람다식이 정의된 컨텍스트(위치)에서 캡처한 변수들(람다식 내부에서 접근할 수 있도록 선언하는 외부변수)을 저장하고 있습니다.std::function은 템플릿 클래스로, 함수 객체의 타입을 저장할 수 있습니다. 따라서 람다식을 std::function 객체로 저장하면, 이 객체를 호출하는 것은 내부적으로 함수 객체를 호출하는 것과 같습니다.
람다식에서 캡처목록을 [&]으로 전체 참조로 선언할 경우, 컴파일러가 컴파일 시 해당 람다식에서 사용하는 외부 변수들을 추론하여 알아서 캡처목록을 생성합니다.
이 경우, 람다식 내부에서 사용하는 모든 외부 변수들을 참조(reference)로 캡처하므로, 외부 변수의 값이 변경되면 람다식 내부에서도 해당 변경된 값을 사용하게 됩니다.
람다식이 std::function을 이용해 파라미터로 넘어갈 때는, 컴파일러가 람다식의 타입을 추론하여 함수 객체의 타입으로 변환합니다. 이렇게 변환된 함수 객체는 std::function 객체에 저장됩니다.
예를 들어, 다음과 같은 코드가 있다고 가정해 봅시다.
#include <iostream>
#include <functional>
void print(std::function<void()> f) {
f();
}
int main() {
int x = 5;
auto lambda = [&x]() {
std::cout << "Captured value: " << x << std::endl;
};
print(lambda);
return 0;
}
위 코드에서 print() 함수는 std::function<void()>를 파라미터로 받습니다. std::function<void()>는 반환값이 없는 함수를 뜻합니다. main() 함수에서는 x 값을 5로 초기화하고, 람다식을 lambda라는 이름으로 저장합니다. print() 함수에 lambda를 전달합니다.
print() 함수에서는 파라미터로 전달된 람다식을 실행합니다. 이 때, lambda가 먼저 함수 객체로 변환되어 저장되고, std::function 객체에 저장됩니다. 그리고 print() 함수가 std::function 객체를 호출하면, 내부적으로 함수 객체가 호출됩니다. 따라서 Captured value: 5라는 출력이 나타나게 됩니다.
이처럼 std::function을 이용해 람다식을 파라미터로 전달하면, 람다식이 포함된 함수 객체를 캡슐화하여 보다 유연한 프로그래밍이 가능해집니다.
std::function은 C++11부터 지원됩니다.
C++11 이전에는 함수 포인터를 사용하여 함수를 가리키는 포인터를 정의하거나, 함수 객체를 사용하여 함수를 처리할 수 있었습니다. 그러나 이러한 방식들은 사용하기에 제약이 많아 유연성이 부족했습니다. C++11에서 도입된 std::function은 이러한 제약을 극복하고, 함수 포인터와 함수 객체를 모두 처리할 수 있는 유연하고 편리한 방법을 제공합니다. 따라서 std::function은 C++11 이후부터 널리 사용되고 있습니다.
void testFun(void (*fptr)(int)) {
(*fptr)(10);
}
void main() {
// 캡쳐목록을 선언한 람다식은 함수포인터로 변경할 수 없습니다.
// 파라미터로 전달하려는 경우 위 코드처럼 std::function을 통해 전달해야 합니다.
testFun([](int a){
cout << a << endl;
});
}