2016-05-31 2 views
0

저는 cpp를 처음 사용하고 람다 함수로 작업하려고합니다. 나는 람다 함수 내부와 외부에서 증가 시키려고하는 카운터를 가지고있다. 내가 이해할 수없는 이상한 메모리 오류가 보이고있다. 다음은이 카운터로 작업하는 흐름입니다. 제가 잘못하고있는 것이 있습니까?C++ 람다 함수 클로저 - 메모리 문제

bool SomeClass::func() { 
    int64_t counter = 0; 

    //some loop logic { 
     counter++; 
    } 


    auto lamb = [this, &counter]() { 
     //some logic 
     counter++; 
    } 

    someotherFunction(data, lamb); // this function will execute the lambda 

} 
+3

'someotherFunction'에서'lamb'을 실행하면'counter'가 여전히 유효하기 때문에이 함수가 작성되어야합니다. 그러나 'someotherFunction'이 람다를 저장하고 나중에 func이 반환 된 후에 람다가 실행되면 매달린 참조 문제가 생깁니다. –

+0

하지만 아래의 주석을 보면 someotherFunction의 실행이 func 실행을 초과하면 올바르게 실행되지 않는 것으로 보입니다. – sublime

+0

요점은 당신이 * inside *'func'에서'someotherFunction'을 호출하고 있다는 것입니다. 즉,'func'보다 더 오래 살 수는 없습니다. –

답변

7

문제는 지역 변수가 어딘가에 저장되어 있기 때문에 (로컬의 "소유자"생존 람다에 의해 참조에 의해 캡처 될 때 C++ 람다는 즉, "upwards funarg"사건을 해결 할 수없는 것입니다 함수 결과로 반환 되었기 때문에).

람다가 참조로 변수를 캡처 할 때 C++는 참조 된 변수의 주소를 람다 코드가 사용하는 컨텍스트 구조에 저장하는 것입니다.

그러나 변수 자체는 지역 변수이며 람다가 만들어진 스택 프레임에 있습니다. 람다 객체가 단지 someOtherFunction의 실행 중에 호출된다면 문제가 없지만, 람다가 이 떨어져서 저장되고에 저장되고 그것을 만든 스택 프레임에서 살아남는다. (또는 람다에서 참조 된 캡쳐 된 변수를 생성했다.)가 실행될 때 더 이상 존재하지 않는 변수를 참조합니다 (정의되지 않은 동작).

일반적으로 "위쪽 funarg"문제를 해결하려면 가비지 수집기가 필요하며 C++에는 가비지 수집기가 필요하지 않습니다.

당신은 어떤 경우에 할 수있는 "값"캡처, 그래서 람다는 자신의 개인 사본있을 것이다 무엇 : 당신은 또한 필요 복사 당신이 캡처 변경하려면 그러나이 경우에

foo([counter]() mutable { counter++; }) 

mutable 키워드를 사용하는 것이 좋습니다 ... 캡처 된 복사본을 수정하려는 경우 C++에 필요하기 때문입니다. 캡처 된 복사본은 람다 본문의 const 개체입니다.

불행히도 캡처 된 변수를 두 개의 람다 (예 : 동일한 캡처 변수에서 "증분 자"와 "감소 자"모두 생성)와 공유해야하는 경우 사본 사용은 실행 가능하지 않습니다. 이 작업을 수행하기 위해 수행 할 수있는 작업은 변수에 std::shared_ptr의 값으로 캡처하는 것이며, 이는 참조 루프의 경우가 아닌 간단한 경우 가비지 수집기를 올바르게 대체합니다.

+0

이 문제를 해결하려면 어떻게합니까?그런 다음 람다에 로컬 변수를 값으로 전달하는 방법이 있습니까? – sublime

+0

공유 포인터를 사용하라고 말했을 때,'auto pin = std :: make_shared (counter);'과'pin' 값을 취하는 말을 했습니까? @ 6502 – Anzurio

+1

@Anzurio : 예. 람다 수명 자체가 캡처 된 객체와 함께 참조 루프를 생성 할 수있는 공유 포인터에 의해 처리되지 않는다면 (C++은 가비지 컬렉터를 제공하지 않으므로 참조 카운트를 포기한 루프가 누수 됨) 값으로 'shared_ptr'을 캡처해도 좋습니다. – 6502

0

는 6502의 훌륭한 답변에 실용적인 접근 방식을 추가 :

사본이 그것을 거기에 의미를 여기
auto make_counter(int initial) { 
    return [initial] (void) mutable { 
    return ++initial; 
    }; 
} 

, (실제로는 함수 매개 변수는하지만 그 중요성이 아니다)이 값에 포착되는 지역 변수 " 안쪽에 "그 람다의 모든 물건. 카운터를 증가시킬 수 있으려면 람다가 캡쳐 된 변수를 수정할 수 있어야하므로 mutable입니다.