결론부터 정리
C++20의 lambda 추가기능
- 람다표현식에서 템플릿 사용 가능
- 평가 되지 않은 표현식에서 람다 표현식 사용가능
- 캠쳐하지 않은 람다 표현식에서 디폴트 생성자와 대입연사자 사용가능
- 암시적인 this, 캡쳐가 deprecated 됨
- parameter pack 캡쳐 가능
말로 설명하면 무슨말인지 모르니 아래에 설명참고
람다표현식에서 템플릿 사용 가능
#include <iostream>
auto add1 = [](int a, int b) { return a + b; };
auto add2 = [](auto a, auto b) { return a + b; };
// generic lambda expression -> C++14부터 가능
int main()
{
std::cout << add1(1, 2) << std::endl; // 3
std::cout << add1(1.1, 2.2) << std::endl; // 3 -> error!
std::cout << add2(1, 2) << std::endl; // 3
std::cout << add2(1.1, 2.2) << std::endl; // 3.3
std::cout << add2(1, 2.2) << std::endl; // 3.2 -> 이걸 에러내고 싶다면??
// 하고싶은것? -> 입력을 같은 타입만 받게 사용하고 싶다
}
auto add3 = [](auto a, decltype(a) b) { return a + b; };
int main()
{
std::cout << add3(1, 2) << std::endl; // 3
std::cout << add3(1.1, 2.2) << std::endl; // 3.3
std::cout << add3(1, 2.2) << std::endl; // error!
}
이것을 람다표현식에서 템플릿로 처리
#include <iostream>
auto add1 = [](auto a, auto b) { return a + b; }; // C++14
auto add2 = []<typename T>(T a, T b) { return a + b; }; // C++20
int main()
{
std::cout << add1(1, 2) << std::endl; // 3
std::cout << add1(1.1, 2.2) << std::endl; // 3.3
std::cout << add1(1, 2.2) << std::endl; // 3.2
std::cout << add2(1, 2) << std::endl; // 3
std::cout << add2(1.1, 2.2) << std::endl; // 3.3
std::cout << add2(1, 2.2) << std::endl; // error!
}
평가 되지 않은 표현식에서 람다 표현식 사용가능
#include <iostream>
#include <memory>
struct Freer
{
inline void operator()(void* p) const noexcept
{
std::cout << "free" << std::endl;
free(p);
}
};
int main()
{
std::unique_ptr<int> up1(new int); // 안전하게 delete 가 불린다.
//std::unique_ptr<int> up2(static_cast<int*>(malloc(100))); // free를 통해야 delete가 된다.
std::unique_ptr<int, Freer> up2(static_cast<int*>(malloc(100))); // 소멸자를 지정해 줘야한다.
}
이걸 좀 람다표현식으로 간단하게 쓸 수 없나?
std::unique_ptr<int, [](int* p){free(p);}> up2(static_cast<int*>(malloc(100)));
// 이렇게 표현하고 싶은데 그냥 람다식은 안들어가고 자료형이 들어가야한다.
std::unique_ptr<int, decltype([](int* p){free(p);})> up2(static_cast<int*>(malloc(100)));
// 이게 C++20부터는 선언가능
#include <iostream>
#include <memory>
int add(int a, int b) { return a + b; }
int main()
{
std::cout << sizeof(int) << std::endl; // 4
std::cout << sizeof(add(1,2)) << std::endl; // 4 (리턴타임을 의미)
decltype(add(1,2)); // int n : 표현식 결과의 타입
std::cout << sizeof( [](int a, int b) { return a+b; }) << std::endl; // 이건 뭐가 나올까?
// C++17까지는 error
// C++20은 동작한다.
std::cout << sizeof( [](int a, int b) { return a+b; }) << std::endl; // 1
std::cout << sizeof( [](int a, int b) { return a+b; }(1,2)) << std::endl; // 4
}
캠쳐하지 않은 람다 표현식에서 디폴트 생성자와 대입연사자 사용가능
#include <iostream>
int main()
{
int v1 = 10;
auto f1 = [](int a, int b){ return a + b; }
// C++11 ~ C++17 C++20
decltype(f1) f2; // Error ok (디폴트 생성자)
decltype(f1) f3 = f1; // ok ok (복사 생성자)
f3 = f1; // Error ok (대입 연산자)
}
#include <iostream>
int main()
{
int v1 = 10;
// 값을 받아 버린다면?
auto f1 = [v1](int a, int b){ return a + b; }
// C++11 ~ C++17 C++20
decltype(f1) f2; // Error Error (디폴트 생성자)
decltype(f1) f3 = f1; // ok ok (복사 생성자)
f3 = f1; // Error Error (대입 연산자)
}
암시적인 this, 캡쳐가 deprecated 됨
#include <iostream>
#include <functional>
struct Sample
{
int value = 0;
auto foo()
{
int n = 10;
// 멤버함수에서 [=]의 의미는 [=] 뿐만아니라 this까지 capture
auto f = [=](int a) { return a + n + value; };
// value라는 멤버데이터는 this가 capture되기에 접근이 가능
std::cout << sizeof(f) << std::endl;
return f;
}
};
std::function<int(int)> f; // 전역으로 function pointer 선언함.
void goo()
{
Sample s;
f = s.foo();
}
int main()
{
goo();
std::cout << f(10) << std::endl;
// C++17 - 컴파일은 되지만 쓰레기 값이 나온다. - goo에서 생성된 Sample은 소멸된지 오래!
// C++20 - warning이 나타난다 - 암묵적 this를 쓰지말라고 경고나옴
}
parameter pack 캡쳐 가능
#include <iostream>
// Capture Parameter pack by value.
template<typename ... Args> auto f1(Args&&... args)
{
return [...args = std:forward<Args>(args)](){ (std::cout << ... << args); };
}
// Capture Parameter pack by reference
template<typename ... Args> auto f2(Args&&... args)
{
return [&...args = std::forward<Args>(args)](){ (std::cout << ... << args); };
}
int main()
{
f1(1,2,3,4,5)();
int a = 1, b = 2, c = 3;
f2(a,b,c)();
}