code

const 변수가 때때로 람다에서 캡처 될 필요가없는 이유는 무엇입니까?

codestyles 2020. 11. 2. 07:59
반응형

const 변수가 때때로 람다에서 캡처 될 필요가없는 이유는 무엇입니까?


다음 예를 고려하십시오.

#include <cstdlib>

int main() {
    const int m = 42;
    [] { m; }(); // OK

    const int n = std::rand();
    [] { n; }(); // error: 'n' is not captured
}

첫 번째 람다가 n아닌 두 번째 람다에서 캡처해야하는 이유는 무엇 m입니까? C ++ 14 표준에서 섹션 5.1.2 ( Lambda 표현식 )를 확인 했지만 이유를 찾을 수 없었습니다. 이것이 설명 된 단락을 알려줄 수 있습니까?

업데이트 : GCC 6.3.1 및 7 (트렁크) 모두에서이 동작을 관찰했습니다. Clang 4.0 및 5 (트렁크)가 두 경우 ( variable 'm' cannot be implicitly captured in a lambda with no capture-default specified) 에서 오류와 함께 실패합니다 .


블록 범위의 람다의 경우 도달 범위의 특정 기준을 충족하는 변수 는 캡처되지 않더라도 람다 내에서 제한된 방식으로 사용될 수 있습니다.

대략적으로 말하면, 도달 범위 에는 람다가 정의 된 지점의 범위에있는 람다를 포함하는 함수에 대한 로컬 변수가 포함됩니다. 이 포함 그래서 m하고 n위의 예이다.

"특정 기준"과 "제한된 방법"은 구체적으로 다음과 같습니다 (C ++ 14 기준).

  • 람다 내에서 변수는 odr-used 가 아니어야합니다. 즉, 다음을 제외하고는 어떤 작업도 수행해서는 안됩니다.
    • 폐기 된 값 표현식으로 표시 ( m;이 중 하나임) 또는
    • 그 가치를 검색했습니다.
  • 변수는 다음 중 하나 여야합니다.
    • const, 비 volatile그의 초기화 정수 또는 열거가 있었다 상수 식 또는
    • constexpr, 비 volatile변수 (또는의 서브 객체)

C ++ 14에 대한 참조 : [expr.const] /2.7, [basic.def.odr] / 3 (첫 번째 문장), [expr.prim.lambda] / 12, [expr.prim.lambda] / 10.

다른 의견 / 답변에서 제안한 바와 같이 이러한 규칙의 근거는 컴파일러가 블록과 무관 한 자유 함수로 캡처되지 않은 람다를 "합성"할 수 있어야한다는 것입니다 (이러한 것들은 포인터로 변환 될 수 있기 때문). to-function); 변수가 항상 동일한 값을 갖는다는 것을 알고 있으면 변수를 참조하더라도이를 수행 할 수 있거나 컨텍스트와 독립적으로 변수의 값을 얻기위한 절차를 반복 할 수 있습니다. 그러나 변수가 수시로 다를 수 있거나 예를 들어 변수의 주소가 필요한 경우에는이를 수행 할 수 없습니다.


귀하의 코드에서 n상수가 아닌 표현식으로 초기화되었습니다. 따라서 n캡처하지 않고는 람다에서 사용할 수 없습니다.

m상수 표현식으로 초기화 42되었으므로 "특정 기준"을 충족합니다. 버려진 값 표현식은 표현식을 사용하지 않으므로 캡처 m;하지 않고 사용할 수 있습니다 m. gcc가 정확합니다.


두 컴파일러의 차이점은 clang은 m;odr-use를 고려 m하지만 gcc는 그렇지 않다는 것입니다. [basic.def.odr] / 3의 첫 번째 문장은 매우 복잡합니다.

변수 x이름이 나타날 잠재적-평가 식 등이 ex있다 ODR으로 사용 하여 ex상기 좌변 투 r- 수치 변환을 적용하지 않는 x수율 경우, 모든 비 단순 함수 호출하지 않는 일정한 표현 x목적, ex의 요소를 elvalue에서 rvalue 로의 변환이에 적용 e되거나 e폐기 된 값 표현식 인 표현식 의 잠재적 결과 집합입니다 .

그러나 자세히 읽으면 그것은 구체적으로 폐기 값 표현하지 않는 것을 언급 않습니다 ODR-사용하는 표현입니다.

C ++ 11의 [basic.def.odr] 버전은 원래 폐기 된 값 표현 케이스를 포함하지 않았으므로 게시 된 C ++ 11에서 clang의 동작이 올 바릅니다. 그러나 C ++ 14에 나타나는 텍스트는 C ++ 11에 대한 결함 ( 문제 712 )으로 허용되었으므로 컴파일러는 C ++ 11 모드에서도 동작을 업데이트해야합니다.


상수 표현식이기 때문에 컴파일러는 다음과 같이 처리합니다. [] { 42; }();

[ expr.prim.lambda ] 의 규칙 은 다음과 같습니다.

람다 표현식 또는 일반 람다 odr의 함수 호출 연산자 템플릿 인스턴스화 (3.2) 또는 도달 범위에서 자동 저장 기간이있는 변수를 사용하는 경우 해당 엔티티는 람다 표현식에 의해 캡처됩니다.

다음은 표준 [ basic.def.odr ] 의 인용문입니다 .

이름이 잠재적으로 평가 된 표현식 ex로 나타나는 변수 x는 lvalue에서 rvalue 로의 변환을 x에 적용하여 상수 표현식 (...)을 생성하거나 e가 폐기 된 값 표현식이 아닌 경우 odr 사용됩니다.

(짧게 유지하기 위해 중요하지 않은 부분을 제거했습니다)

내 간단한 이해는 다음과 같습니다. 컴파일러는 이것이 m컴파일 타임에 일정 하다는 것을 알고 있지만 n런타임에는 변경되므로 n캡처해야합니다. n실제로 n런타임에 내부 내용을 확인해야하기 때문에 odr이 사용 됩니다. 즉, "하나만있을 수있다"는 정의 n가 적절합니다.

이것은 MM의 의견입니다.

m은 상수 표현식 이니셜 라이저가있는 const 자동 변수이기 때문에 상수 표현식이지만, 이니셜 라이저가 상수 표현식이 아니기 때문에 n은 상수 표현식이 아닙니다. 이것은 [expr.const] /2.7에서 다룹니다. 상수 표현식은 [basic.def.odr] / 3의 첫 번째 문장에 따라 ODR을 사용하지 않습니다.

데모는 여기를 참조하십시오 .


편집 : 내 대답의 이전 버전이 잘못되었습니다. 초보자가 정확합니다. 여기에 관련 표준 견적이 있습니다.

[basic.def.odr]

  1. A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion to x yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion is applied to e, or e is a discarded-value expression. ...

Since m is a constant expression, it is not odr-used and therefore does not need to be captured.

It appears that clangs behaviour is not compliant with the standard.

참고URL : https://stackoverflow.com/questions/43467095/why-is-a-const-variable-sometimes-not-required-to-be-captured-in-a-lambda

반응형