C++20에서는 람다 표현식(lambda expressions)이 더욱 강력해지고 유연해졌습니다. 이번 글에서는 개선된 람다 캡처(Lambda Capture)와 관련된 새로운 기능들을 살펴보겠습니다.
개선된 람다 캡처란?
C++11에서 도입된 람다 표현식은 익명 함수 객체를 생성하여 함수처럼 사용할 수 있게 해주는 기능입니다. 기존의 람다 캡처 방식에서는 [=], [&], [this] 등의 캡처 모드를 사용했습니다. C++20에서는 람다 캡처가 더욱 개선되어, [=, this], [*this]와 같은 새로운 캡처 방식이 도입되었습니다. 이를 통해 람다 표현식 내에서 멤버 변수를 안전하고 효율적으로 사용할 수 있습니다.
[*this]를 사용한 객체 복사 캡처
기존의 문제점
기존의 [=] 또는 [this] 캡처 방식은 람다 표현식 내에서 this 포인터를 복사하거나 참조합니다. 하지만 this 포인터는 객체를 참조하므로, 객체가 소멸된 후에 람다를 호출하면 미정의 동작(undefined behavior)이 발생할 수 있습니다.
[*this]를 사용한 개선
[*this] 캡처를 사용하면 현재 객체를 복사하여 람다 표현식에 캡처합니다. 이를 통해 객체의 수명과 관계없이 안전하게 람다를 사용할 수 있습니다.
사용 예제
#include <iostream>
#include <string>
#include <functional>
class Person {
public:
Person(std::string name) : name_(name) {}
auto getGreeter() const {
return [*this]() {
std::cout << "안녕하세요, " << name_ << "입니다.\n";
};
}
private:
std::string name_;
};
int main() {
auto greeter = Person("홍길동").getGreeter();
greeter(); // 출력: 안녕하세요, 홍길동입니다.
return 0;
}
- [*this]를 사용하여 Person 객체를 복사하여 캡처합니다.
- Person 객체가 소멸된 후에도 람다 내에서 안전하게 name_ 멤버에 접근할 수 있습니다.
[=, this]를 사용한 멤버 접근
기존의 제한 사항
기존의 [=] 캡처 모드는 람다 표현식 내에서 지역 변수를 복사로 캡처합니다. 그러나 클래스 멤버에 접근하려면 this 포인터가 필요하며, [=]로는 this를 캡처하지 않습니다.
[=, this]를 사용한 개선
C++20에서는 [=, this] 캡처를 사용하여 지역 변수는 복사로, this 포인터는 참조로 캡처할 수 있습니다.
사용 예제
#include <iostream>
class Counter {
public:
Counter(int start) : count_(start) {}
void startCounting(int limit) {
int step = 1;
auto counter = [=, this]() {
for (; count_ <= limit; count_ += step) {
std::cout << "현재 값: " << count_ << '\n';
}
};
counter();
}
private:
int count_;
};
int main() {
Counter c(0);
c.startCounting(3);
return 0;
}
- [=, this]를 사용하여 step 변수는 복사로 캡처하고, this 포인터는 참조로 캡처합니다.
- 람다 내에서 count_ 멤버 변수에 접근할 수 있습니다.
람다의 템플릿 사용
람다 표현식의 템플릿 매개변수
C++20에서는 람다 표현식이 템플릿 매개변수를 가질 수 있게 되었습니다. 이를 통해 더욱 일반화된 람다 함수를 작성할 수 있습니다.
사용 예제
#include <iostream>
auto adder = []<typename T>(T a, T b) {
return a + b;
};
int main() {
std::cout << adder(1, 2) << '\n'; // 출력: 3
std::cout << adder(1.5, 2.5) << '\n'; // 출력: 4.0
std::cout << adder(std::string("Hello, "), std::string("World!")) << '\n'; // 출력: Hello, World!
return 0;
}
- 람다 표현식에 템플릿 매개변수 <typename T>를 추가하여 다양한 타입에 대해 동작합니다.
constexpr 람다
람다의 constexpr 지원 강화
C++20에서는 람다 표현식이 constexpr로 선언되면, 람다의 호출 연산자도 constexpr이 됩니다. 이를 통해 컴파일 타임에 람다를 평가할 수 있습니다.
사용 예제
constexpr auto square = [](int x) {
return x * x;
};
int main() {
constexpr int result = square(5);
static_assert(result == 25, "결과가 25가 아닙니다.");
return 0;
}
- constexpr 람다를 사용하여 컴파일 타임에 값을 계산하고, static_assert로 검증합니다.
noexcept 람다
람다의 noexcept 지정
C++20에서는 람다 표현식에 noexcept를 지정할 수 있습니다.
사용 예제
auto safe_divide = [](double a, double b) noexcept -> double {
return b != 0 ? a / b : 0.0;
};
int main() {
std::cout << safe_divide(10.0, 2.0) << '\n'; // 출력: 5.0
std::cout << safe_divide(10.0, 0.0) << '\n'; // 출력: 0.0
return 0;
}
- noexcept를 사용하여 람다가 예외를 throw하지 않음을 명시합니다.
요약
C++20에서의 개선된 람다 캡처와 관련 기능들은 람다 표현식을 더욱 강력하고 유연하게 만들어 줍니다. 특히 [*this]와 [=, this] 캡처 방식은 클래스 멤버 변수의 안전한 사용을 가능하게 하며, 람다의 템플릿 매개변수 지원은 제네릭 프로그래밍을 더욱 편리하게 해줍니다.
참고 자료:
'개발 이야기 > C++' 카테고리의 다른 글
[C++20 새기능 소개] using enum (0) | 2024.12.05 |
---|---|
[C++20 새기능 소개] std::span (0) | 2024.12.04 |
[C++20 새기능 소개] std::format 라이브러리 (32) | 2024.12.02 |
[C++20 새기능 소개] consteval과 constinit 키워드 (30) | 2024.12.01 |
[C++20 새기능 소개] 지정 초기화자(Designated Initializers) (19) | 2024.11.30 |