[Basic C++] #45_람다 표현식 활용, 제네릭 람다 표현식, 람다 캡처 표현식, 리턴 타입으로서 람다 표현식
STL 알고리즘 중 "람다 표현식"에 대해 알아보겠습니다.
"전문가를 위한 C"의 17 항목, "STL 알고리즘 마스터하기"에 해당하는 내용입니다.
Overview
- STL 알고리즘과 람다 표현식
- 캡처 블록 내 초기화
- std::function
- 콜백 람다 표현식
#0. STL 알고리즘과 람다 표현식
1. 제네릭 알고리즘 + 람다 표현식
- STL이 제공하는 제네릭 알고리즘의 Predicate(함수 객체)로 활용할 수 있습니다.
2. 예제
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> intV{ 11, 55, 101, 200 };
vector<double> doubleV{ 11.1, 55.5, 200.2 };
// 람다 표현식 == Predicate
auto isGreaterThan100 = [](auto i) -> bool {return i > 100; };
// find_if(구간의 첫 번째, 구간의 마지막 직후, Predicate)
auto it1 = find_if(cbegin(intV), cend(intV), isGreaterThan100);
// integer vector
if (it1 != cend(intV))
cout << "Greater than 100 : " << *it1 << endl;
cout << "\n";
// double vector
auto it2 = find_if(cbegin(doubleV), cend(doubleV), isGreaterThan100);
if (it2 != cend(doubleV))
cout << "Greater than 100: " << *it2 << endl;
}
* 결과
Details
- 람다 표현식을 제네릭 알고리즘의 Predicate(함수 객체)로 활용하는 예제입니다.
#1. 캡처 블록 내 변수 초기화
1. 캡처 블록 내 변수 선언
- "람다 표현식"의 캡처 블록 안에서 캡처된 변수는 어떤 종류의 표현식으로도 초기화될 수 있습니다.
- 아래 코드를 먼저 살펴보겠습니다.
2. 예제 코드 1
#include <iostream>
using namespace std;
int main()
{
double pi = 3.1415;
auto lam = [myStr = "Pi is : ", pi]() { cout << myStr << pi << endl; };
lam();
}
* 결과 화면
Details
- 위 코드 예제의 "myStr"은 람다 표현식의 캡처 블록 내에서 초기화되어 활용되고 있습니다.
3. 예제 코드 2
#include <iostream>
using namespace std;
int main()
{
// unique_ptr : 복제 될 수 없는 스마트 포인터
auto myUniquePtr = make_unique<double>(3.1415);
auto lam = [p = move(myUniquePtr)](){cout << *p << endl; };
lam();
}
* 결과 화면
Details
- 람다 표현식의 캡처 블록 안에서 우리는 "std::move()"도 사용 가능합니다.
- 위 코드 예제를 살펴보면, "unique_ptr"을 캡처 변수로 선택합니다.
- 이때, "unique_ptr"의 성격상 값에 의한 복제가 불가능하기 때문에, "std::move()"를 통해 이동 시맨틱으로 변경해 주는 것을 확인할 수 있습니다.
#2. std::function
1. 개념
- std::function은 호출 가능한 어떠한 유형의 객체도 홀드 혹은 감쌀 수 있는 템플릿 클래스입니다.
2. 장점
#include <iostream>
#include <functional>
int add(int x, int y) {
return x + y;
}
int main() {
// #1. 함수 포인터
std::function<int(int, int)> func1 = add; // function pointer
// #2-1. 람다 표현식
std::function<int(int)> func2 = [](int x) { return x * x; }; // lambda function
// #2-2. 람다 표현식
std::function<double(double)> func3 = [](double x) { return x / 2.0; }; // lambda function
// #3. 가변 인자 함수, variadic function
std::function<int(int, ...)> func4 = printf;
std::cout << func1(2, 3) << std::endl; // Output: 5
std::cout << func2(4) << std::endl; // Output: 16
std::cout << func3(7.0) << std::endl; // Output: 3.5
f3("%d, %d %d\n", 1, 2, 3); // Output : "1 2 3"
return 0;
}
Details
- 먼저, std::function 템플릿 클래스는 동일한 시그니처를 갖는 서로 다른 유형의 객체들을(함수 포인터, 함수 객체, 람다 표현식, C 스타일의 가변 인자 함수) 모두 담을 수 있으며 기존의 호출 방식 혹은 인터페이스를 유지하므로, 유연합니다.
- 유연함, 타입 안정성, 다형성, 그리고 콜백 함수 기능 등을 제공합니다.
3. 예제 코드
#include <iostream>
#include <functional>
using namespace std;
// function<int(void)> 타입의 함수 정의
function<int(void)> multiplyBy2Lambda(int x)
{
// return[&]() {return 2 * x; }; == 에러 발생!
return [x]() {return 2 * x; };
}
int main()
{
function<int(void)> f1 = multiplyBy2Lambda(5);
cout << f1() << endl;
}
* 결과 화면
- 위 코드 예제는 "람다 표현식"을 함수의 리턴 타입으로 활용하기 위해 "std::function" 클래스를 활용했습니다.
- "f1" 변수는 "function <int(void)> 타입으로 "multiplyBy2 Lambda" 메서드의 결과 값을 받아서,
- 호출 연산자를 통해 결과를 화면에 출력하고 있습니다.
- * 주의 : 람다 표현식의 실행 시점은 함수가 반환 값을 리턴한 이후에 실행되며, 캡처 변수를 "참조형"으로 설정할 경우 실행되는 시점에 참조할 변수가 이미 존재하지 않기 때문에 에러를 발생시킬 수 있습니다!
#3. 콜백 함수
1. 예제 코드
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
// 람다 표현식을 파라미터로 활용하기위해 "std::function" 래퍼 클래스 활용
void testCallback(const vector<int>& vec, const function<bool(int)>& callbackLam)
{
for (const auto& val : vec)
{
if (callbackLam(val) != false)
break;
else
cout << val << " ";
}
cout << endl;
}
int main()
{
vector<int> v{ 4, 5, 6, 7, 8, 9 };
// testCallback 함수 호출
testCallback(v, [](int i) { return i > 6; });
}
* 결과 화면
Details
- 앞서 설명했듯이, 람다 표현식을 std::function 유형의 객체에 저장하면, 콜백 함수로 활용할 수 있습니다.
'언어 > Basic C++' 카테고리의 다른 글
[Basic C++] #47_함수 객체 (0) | 2022.07.08 |
---|---|
[Basic C++] #46_람다 표현식과 STL 알고리즘 (0) | 2022.07.07 |
[Basic C++] #44_람다 표현식, 기본 (0) | 2022.07.05 |
[Basic C++] #43_STL 알고리즘, find, find_if (0) | 2022.07.05 |
[Basic C++] #42_unordered_multimap, 비순차 연관 컨테이너, 중복 허용 (0) | 2022.06.28 |