항목 31 : 기본 캡처 모드를 피하자
기본 캡처 모드란 =이나 &로 모든 값, 참조를 캡처하는 경우를 말한다.
우선 당연히 어떤 요소를 캡처하는지 정확히 명시하지 않기에 디자인상 좋지 않다. 참조 캡처의 경우 캡처 대상들의 수명을 잘 관리해야하는데 이는 치명적이다.
하지만 더 큰 문제는 값 캡처의 경우에도 수명 관리를 고려해야할 수 있다는 점이다.
기본적으로 멤버 함수에서 객체의 멤버는 명시하여 캡처할 수 없다. 하지만 =로 전체 값 캡처를 하면 객체의 멤버에 접근이 가능해진다. 이는 this 포인터가 캡처되고 람다 내에서의 멤버 접근이 자동으로 this->멤버로 수정되기에 가능하다. 그리고 그것은 객체와 객체의 멤버를 참조하므로 객체가 소멸된 상태면 문제를 일으킬 수 있다. 멤버 변수를 진짜 값 캡처하고 싶다면 지역 변수에 복사후 캡처하거나 초기화 캡처를 사용하자.
추가로 static 지역 변수의 경우 =로 캡처하면 접근이 가능해진다. 하지만 값이 아닌 레퍼런스가 캡처되므로 마찬가지로 조심해야한다.
항목 32 : 초기화 캡처
초기화 캡처는 C++14에 추가된 기능으로 [x = 10]과 같이 람다에서 사용할 클로저 클래스의 멤버 이름과 초기화 구문으로 캡처할 수 있는 기능이다. 해당 멤버는 람다내에서 사용가능하며 초기화 구문을 자유롭게 사용할 수 있다.
이때 왼변은 클로저의 멤버 범위이고 오른쪽은 람다를 생성한 지역 범위임을 명심하자.
초기화 캡처를 통해 std::move등을 이용해서 이동으로 캡처하는 등의 기법이 가능하다.
C++11의 경우에는 직접 정의한 함수 객체나 std::bind로 이를 흉내낼 수 있다.
항목 33 : 람다 auto&&
람다에서 auto를 매개변수 타입으로 하면 클로저 클래스에서 실행 함수를 템플릿으로 만들어 여러 타입에 대해 람다 호출이 가능해진다.
이때 auto&& x와 같은 식으로 보편 참조도 가능한데 내부에서 std::forward<decltype(x)>(x)로 전달이 가능하다. 기존에 forward의 타입 매개변수에 참조 축약에 의해 T가 넘어가면 RValue, T&가 넘어가면 LValue를 리턴하여는 것으로 템플릿에서 완벽전달을 구현하였는데 forward의 정의와 참조 축약을 고려하면 T&&가 넘어가는 경우에도 RValue 리턴된다는 것을 알 수 있다. 따라서 위와 같이 decltype으로 forward를 사용할 수 있다.
항목 34 : std::bind 보다 람다를 선호하자.
이미 있는 함수에 일부 매개변수를 고정하여 호출할 함수 객체를 만들 목적등으로 std::bind와 람다 중 고민 할 수 있는데 가독성 측면에서 람다가 좋기에 람다를 쓰자.
C++11의 경우 초기화 캡처가 없기에 std::bind로 이를 구현하는 경우가 있어 bind가 나은 경우도 있을 수 있지만 c++14에서는 람다가 무조건 좋다.
'프로그래밍 언어 > Effective Modern C++' 카테고리의 다른 글
[Effective Modern C++] 항목 41 ~ 42 : 다듬기 (1) | 2024.12.15 |
---|---|
[Effective Modern C++] 항목 35 ~ 40 : 동시성 API (1) | 2024.12.15 |
[Effective Modern C++] 항목 23 ~ 30 : 이동, 완벽 전달 (1) | 2024.11.23 |
[Effective Modern C++] 항목 18~22 : 스마트 포인터 (0) | 2024.11.10 |
[Effective Modern C++] 항목 7~17 : 현대적 C++ (2) | 2024.11.03 |