본문 바로가기

PS/기타

lambda함수 내에서 vector<bool> 사용 시 주의사항

#include <bits/stdc++.h>
using namespace std;

int main() {

    auto check = [&](int x) {
        vector<bool> dp(n + 1, 0);
        // ~~~코드들~~~
        return dp[n];
    };

    if (check(0)) cout << "YES";
    else cout << "NO";

    return 0;
}

위 코드는 놀랍게도 UB입니다.

애초에 저런 코드를 작성할 일이 있냐? 싶을수도 있지만.. ps에서 parametric search문제를 풀 때 람다함수를 사용하는 경우가 꽤 많죠..

 

std::vector는 공간 최적화를 위해 일반적인 컨테이너와 다르게 동작합니다. 원래 bool은 1byte지만, vector<bool>의 각 요소는 1바이트가 아닌 1비트로 저장됩니다. std::bitset과 같습니다.

문제는 이로 인해 특정 요소의 주소를 직접 가리키는 bool&을 반환할 수 없다는 점입니다. C++에서 주소 값은 바이트 단위로 매겨지는데, 각 요소가 비트 단위로 저장되어있으니 어쩔 수 없죠. 이로 인해 dp[idx]를 호출하면 실제 bool& 대신 std::vector::reference라는 프록시 객체가 반환됩니다.

어차피 프록시 객체를 사용하더라도 읽을 때는 bool로 형변환되기에 보통은 문제를 겪을 일이 없지만, 람다함수와 사용할 경우 조심할 점이 있습니다.

 

가장 큰 문제는 vector dp가 람다함수 내부에서 선언된 지역변수이므로 함수 밖에선 접근할 수 없다는 점입니다.

만약 bool f()처럼 전역함수를 사용했다면 애초에 반환타입이 bool이므로 함수 내부에서 return dp[n]을 할 때 프록시 객체가 bool 값으로 바뀌어 리턴될 것이므로 괜찮습니다.

 

하지만 위 코드에서는 람다함수의 반환타입을 지정하지 않았으므로, 컴파일러는 return dp[n]으로부터 반환 타입을 std::vector::reference로 추론합니다. dp 벡터는 check 함수 내부에 선언된 지역 변수이므로 함수가 종료되는 순간 스택에서 해제되어 사라지는데, check함수의 반환값은 사라진 dp배열의 특정 비트 위치를 가리키는 프록시 객체입니다. 즉 Dangling Reference 상태가 되고, 따라서 check(x)의 값을 확인하는 순간 UB입니다.

 

이를 해결하려면 auto check = [&](int x) -> bool {}와 같이 람다의 반환타입을 bool로 명시하거나 return bool(dp[n]);을 사용하면 됩니다.

가장 좋은 방법은 람다함수를 사용할 때 항상 반환타입을 명시하는 습관을 들이는 것이겠지만, 생각보다 귀찮아서 생략하는 경우가 많긴 하죠..

 

https://www.acmicpc.net/problem/16286 에러를 겪었던 문제입니다. 

'PS > 기타' 카테고리의 다른 글

시간복잡도  (0) 2025.03.29
백준 팁  (0) 2025.03.15