MISRA C++-2023 Rule 8.1.1 / 8.1.2 — 람다 캡처: this vs &

규칙 요지

  • 8.1.1: 람다는 사용하는 캡처만 명시적으로 나열해야 한다 (default capture [&], [=] 금지)
  • 8.1.2: this를 캡처할 때도 암묵적(default capture로 딸려오는) 방식이 아니라 명시적으로 [this]를 적어야 한다

[&]와 [this]는 다른 층위의 캡처다

캡처 대상문제점
[&]람다 본문에서 쓰는 모든 외부 변수 + (멤버 접근 시) this까지 자동 참조 캡처코드만 봐선 뭘 캡처하는지 안 보임. 나중에 코드가 바뀌면 의도치 않은 변수가 조용히 캡처되거나, 캡처된 참조 대상이 람다 실행 시점엔 이미 소멸된 상태(dangling reference)일 수 있음 — 특히 비동기 콜백(loop.post)에서 위험
[this]this 포인터 하나만 명시적으로 캡처 → 멤버 변수/멤버 함수 접근용필요한 걸 딱 명시하므로 리뷰/정적분석 시 캡처 범위가 명확

즉 [&]는 “this를 포함해 눈에 안 보이는 모든 것”을 캡처하고, [this]는 “this 하나만” 명시적으로 캡처한다. 멤버 함수 호출만 필요한 상황이라면 [this]가 정확히 필요한 만큼만 캡처하는 방법이다.

왜 this가 이번 케이스의 답이었나

  • 이 PR의 람다들은 전부 RoutineScheduler/Trigger의 멤버 함수(handle_command_, start_monitor_ 등)를 호출
  • 해당 객체는 이벤트 루프(loop.post) 콜백이 실행되는 시점까지 항상 살아있음이 구조적으로 보장됨 (스케줄러 자신이 루프를 돌리는 주체)
  • 따라서 this 포인터 캡처만으로 충분하고 안전함

this 외의 대안 (참고용, 이번엔 불필요)

  • [*this] (C++17): 객체 전체를 값으로 복사 캡처. 원본 수명과 무관해지지만 복사 비용 + 상태 불일치 가능성 발생. 원본 객체 수명이 불확실한 진짜 비동기 상황에서나 고려 대상
  • 멤버를 로컬 참조로 미리 뽑아서 캡처: auto& x = this->member_; [&x]{...} — this 전체 대신 필요한 멤버 하나만 좁혀서 캡처. C++에서 캡처 리스트에 멤버 변수를 직접 못 적기 때문에([member_] 불가) 이 alias 방식을 쓴다

이번 PR은 “동작 무변경 + 스타일 수정만”이 목표였기 때문에, 위 두 대안 대신 가장 단순하고 안전한 [this, &loop] 형태의 명시적 캡처를 선택했다.

결론

  • [&]와 [this]는 “같은 걸 캡처하는 다른 표현”이 아니라, 캡처 범위 자체가 다름 ([&] ⊃ [this])
  • MISRA 8.1.1/8.1.2는 “암묵적으로 넓게” 캡처하지 말고 “명시적으로 필요한 만큼만” 캡처하라는 요구
  • 멤버 함수/변수 접근이 목적이면 this(또는 *this, 또는 멤버 alias)가 정답이고, &는 그 외 지역 변수(loop, state 등)까지 몰아서 캡처하는 별개의 것