2012-09-20 1 views
12

두 개의 로컬 스마트 포인터, foobar이 있다고 가정 해 보겠습니다.C++ 11 : 람다 캡처는 어떤 순서로 파괴 되었습니까?

shared_ptr<Foo> foo = ... 
shared_ptr<Bar> bar = ... 

이 스마트 포인터는 어떤 이유로 위해서는 다음 foo, bar에 파괴해야 자원 래퍼입니다.

이제는 foobar을 사용하지만 그 범위를 초과하는 람다를 만들고 싶습니다. 그래서 다음과 같이 값을 캡처 할 것 :이 함수 객체 내에서 foobar의 복사본을 생성

auto lambda = [foo, bar]() { ... }; 

. 함수 객체가 파괴 될 때,이 사본들은 파괴 될 것이지만, 나는 이런 일이 일어나는 순서에주의를 기울인다. 그래서 내 질문은 다음과 같습니다.

람다 객체가 파괴되면, 그 값에 의한 값 캡처가 파괴 된 순서는 무엇입니까? 그리고 어떻게이 순서에 영향을 줄 수 있습니까?

+4

나는 또한 고려하는 것이 재미있을 거라고 생각'[=]'. –

+0

@ R.MartinhoFernandes :'[foo, bar]'는'[= foo, = bar]와 동일합니다. 즉 사본입니다. –

+0

@David : 그는 문자 그대로 '[=]'을 의미한다고 생각합니다. 즉, 변수 자체를 나열하지 않고 선언 순서를 고려하십시오. (분명히 선언 순서가 캡처 방법에 관계없이 지정되지 않았으므로 이제 분명하지 않습니다.) – ildjarn

답변

15

이 사양은 다음을 포함합니다. 5.1.2에서, 14 항 :이 암시 적으로 캡처 된 경우 엔티티가 사본 캡처

및 캡처 - 기본값은 = 또는 명시 적으로 캡처로 캡처되는 경우 & 포함되지 않습니다. 카피에 의해 캡쳐 된 각 엔티티에 대해, 명명되지 않은 비 정적 데이터 멤버가 클로저 타입으로 선언됩니다. 이 멤버의 선언 순서는 지정되지 않습니다.

강조가 추가되었습니다. 선언 순서가 지정되지 않았기 때문에 (순서가 선언 순서와 같기 때문에) 순서가 지정되지 않습니다. 따라서 파괴 순서는 건설 순서와 반대이므로 파괴 순서는 지정되지 않았습니다.

간단히 말해서 선언 명령 (및 그로부터 떨어져 나오는 여러 가지 건설/파괴 명령)을 염려 할 필요가 있다면 은 람다를 사용할 수 없습니다. 자신 만의 유형을 만들어야합니다.

+1

람다 끝에서 공유 포인터 중 하나를 재설정하여 리소스를 해제 할 수 없었습니까? 파괴 중인가? –

+0

Nevermind; 내 대답을 보라. 람다는 그것이 작동하기 위해서는 변경 가능해야합니다. –

+0

@ 니콜 : 설명해 주셔서 고맙습니다. 나는 그들이 주문을 명기하지 않았다는 것을 당황스럽게 생각한다 - 일반적으로 C++에서는 건설 및 파괴 명령과 관련된 모든 것에 대해 잘 규정되어있다. 그러나 적어도 지정되지 않았기 때문에 특정 컴파일러가 사용하는 순서에 의존하지 않습니다. –

3

내가 가지고있는 C++ 11 문서 (예 : 공짜로, 약간 비평 이전의 n3242), 섹션 5.1.2, para 21에 따르면 캡처는 선언 순서로 생성되고 반대 선언 순서로 파괴됩니다 . 그러나 선언 순서는 명시되어 있지 않다 (문단 14). 따라서 대답은 "불명확 한 순서"와 "영향을 미칠 수 없다"입니다 (컴파일러 작성을 제외하고).

bar가 foo보다 먼저 destruct 될 필요가 있다면, bar가 foo (또는 뭔가 종류)에 대한 공유 포인터를 보유하는 것이 현명 할 것입니다.

8

파괴 순서가 무엇인지 걱정하지 말고 이것이 문제라는 사실을 고쳐야합니다. 두 개체에 대해 공유 포인터를 사용하고 있음을 알면 다른 개체에서 오래 살아야 할 개체에 공유 포인터를 추가하여 파기 순서를 확인할 수 있습니다. 이 시점에서 foo 또는 bar이 먼저 파괴되는지 여부는 중요하지 않습니다. 순서가 올 바르면 공유 포인터를 삭제하면 개체가 즉시 해제됩니다. 순서가 올바르지 않으면 추가 공유 포인터는 다른 개체가 사라질 때까지 개체를 활성 상태로 유지합니다.

8

니콜 (Nicol)이 말했듯이 파괴의 순서는 지정되지 않았습니다.

그러나 람다의 파괴에 의존하지 않아도됩니다. 당신은 람다의 끝에서 foo을 간단히 다시 설정할 수 있습니다. 따라서 bar 전에 리소스를 해제해야합니다. 또한 람다를 mutable으로 표시해야합니다. 여기서 단 한 가지 단점은 람다를 여러 번 호출하여 작동 할 수 없다는 것입니다.


당신이 호출 여러 번으로 귀하의 람다를해야하는 경우

auto lambda = [foo, bar]() mutable { ...; foo.reset(); }; 

는, 당신은 해제의 순서를 제어하는 ​​다른 방법을 마련 할 필요가있다. 하나의 옵션은 같은 std::pair<>로 알려진 데이터 멤버 순서로 중간 구조를 사용하는 것입니다 :

auto p = std::make_pair(bar, foo); 
auto lambda = [p]() { auto foo = p.second, bar = p.first; ... }; 
+0

필자의 경우, 람다는 실제로 한 번만 실행되도록 보장되므로 수동 재설정 방법이 제대로 작동해야합니다. shared_ptr이 가리키고있는 것을 풀어주기 위해서, 반드시 그것을 destuct 할 필요는 없다는 것을 잊었습니다. 고마워요! –