같은 이름의 Lambda 캡처 및 매개 변수-누가 다른 사람을 섀도 잉합니까? (clang 대 gcc)


125
auto foo = "You're using g++!";
auto compiler_detector = [foo](auto foo) { std::puts(foo); };
compiler_detector("You're using clang++!");
  • clang ++ 3.6.0 이상 은 "You 're using clang ++!"을 출력합니다. 캡처 foo 가 사용되지 않았 음을 경고합니다 .

  • g ++ 4.9.0 이상 은 "You 're using g ++!"을 출력합니다. 사용되지 않는 매개 변수 foo 에 대해 경고합니다 .

여기서 C ++ 표준을 더 정확하게 따르는 컴파일러는 무엇입니까?

지팡이 상자 예


1
wandbox에서 여기로 코드를 붙여 넣으면 (공유 버튼을 잊은 것 같음) VS2015 (?)가 C4458 : 'foo'선언이 클래스 멤버를 숨 깁니다 .
nwp

12
좋은 예 ..
deviantfan

4
람다에는 템플릿 함수 호출 연산자가있는 유형이 있으므로 논리에 따라 매개 변수가 struct Lambda { template<typename T> void operator()(T foo) const { /* ... */ } private: decltype(outer_foo) foo{outer_foo}; }.
skypjack

2
@nwp VS가 잘못되었으며 람다의 데이터 멤버는 이름이 지정되지 않았으므로 섀도 잉 할 수 없습니다. 표준에 따르면 "캡처 된 엔터티에 대한 액세스는 해당 데이터 멤버에 대한 액세스로 변환됩니다"라고되어 있으며, 이는 우리를 정사각형으로 남겨 둡니다.
n. '대명사'm.

10
clang 버전이 정확하기를 바랍니다. 함수 외부의 무언가가 함수 매개 변수에 그림자를 드리 우면 그 반대가 아닌 새로운 영역을 개척 할 것입니다!
MM

답변:


65

업데이트 : 하단 인용문에서 Core 의자가 약속 한대로 코드는 이제 잘못된 형식입니다 .

단순 캡처식별자lambda-declaratorparameter-declaration-clause 매개 변수의 declarator-id 로 나타나면 프로그램의 형식이 잘못된 것입니다.


얼마 전에 람다에서 이름 조회와 관련된 몇 가지 문제가있었습니다. N2927에 의해 해결되었습니다 .

새로운 문구는 더 이상 캡처 된 엔티티의 사용을 다시 매핑하기 위해 조회에 의존하지 않습니다. 람다의 복합 문 이 두 번의 패스로 처리되거나 해당 복합 문의 이름 이 클로저 유형의 멤버로 해석 될 수 있다는 해석을 더 명확하게 거부합니다 .

조회는 항상 lambda-expression 의 컨텍스트에서 수행되며 클로저 유형의 멤버 함수 본문으로의 변환 "이후"가 아닙니다. [expr.prim.lambda] / 8 참조 :

람다 식화합물 문 수율 함수 바디 함수 호출 연산자 ([dcl.fct.def])로하지만, 이름 조회를 위하여, [...] 상기 화합물 문 의 맥락에서 고려 람다 표현 . [ :

struct S1 {
  int x, y;
  int operator()(int);
  void f() {
    [=]()->int {
      return operator()(this->x+y);  // equivalent to: S1::operator()(this->x+(*this).y)
                                     // and this has type S1*
    }; 
  }
};

최종 예 ]

(이 예는 또한 조회가 클로저 유형의 생성 된 캡처 멤버를 어떻게 든 고려하지 않음을 분명히합니다.)

이름 foo은 캡처에서 (재) 선언되지 않습니다. 람다 식을 둘러싸는 블록에서 선언됩니다. 매개 변수 foo는 해당 외부 블록에 중첩 된 블록에서 선언됩니다 ( 람다 매개 변수도 명시 적으로 언급하는 [basic.scope.block] / 2 ). 조회 순서는 내부 블록에서 외부 블록으로 명확 합니다. 입니다. 따라서 매개 변수를 선택해야합니다. 즉, Clang이 맞습니다.

캡처를 init-capture, 즉 foo = ""대신 으로 만들면 foo대답이 명확하지 않습니다. 이것은 지금 캡처가 실제로 "블록"이 제공되지 않은 선언을 유도 입니다. 나는 이것에 대해 핵심 의장에게 메시지를 보냈다.

이 문제는 2211 호입니다 (불행하게도 여러 문제에 대한 자리 표시 자만있는 상태로 곧 open-std.org 사이트에 새로운 문제 목록이 표시됩니다.이 중 하나입니다. 저는 Kona 이전에 이러한 공백을 채우기 위해 열심히 노력하고 있습니다. 월말 회의). CWG는 1 월 원격 회의에서이 문제를 논의했으며 , 캡처 이름이 매개 변수 이름 인 경우 프로그램을 잘못 구성하는 것이 방향입니다.


여기서 뜯어 낼 것은 없습니다 :) 단순 캡처 는 아무것도 선언하지 않으므로 이름 조회의 올바른 결과는 상당히 분명합니다 (BTW, GCC는 명시 적 캡처 대신 캡처 기본값 을 사용하는 경우 올바르게 가져옵니다 ). init-capture 는 다소 까다 롭습니다.
TC

1
@TC 동의합니다. 핵심 문제를 제기했지만 이미 논의 된 것 같습니다. 편집 된 답변을 참조하세요.
Columbo 2017

6

나는 당신에게 의미있는 대답을 제공하기 위해 질문에 대한 몇 가지 의견을 모으고 있습니다.
먼저 다음 사항에 유의하십시오.

  • 복사 캡처 된 각 변수의 람다에 대해 비 정적 데이터 멤버가 선언됩니다.
  • 특정 경우에 람다는 다음과 같은 매개 변수를 허용하는 공용 인라인 템플릿 함수 호출 연산자가있는 클로저 유형을 갖습니다. foo

따라서 논리는 매개 변수가 다음과 같이 캡처 된 변수를 섀도 잉해야한다고 언뜻보기에 말하게합니다.

struct Lambda {
    template<typename T> void operator()(T foo) const { /* ... */ }
    private: decltype(outer_foo) foo{outer_foo};
};

어쨌든 @nm은 복사 캡처 변수에 대해 선언 된 비 정적 데이터 멤버가 실제로 이름이 지정되지 않았다고 올바르게 지적했습니다. 즉, 이름이 지정되지 않은 데이터 멤버는 여전히 식별자 (즉 foo)를 통해 액세스됩니다 . 따라서 함수 호출 연산자의 매개 변수 이름은 여전히 해당 식별자를 숨겨야합니다 .
질문에 대한 주석에서 @nm가 올바르게 지적한대로 :

원래 캡처 된 엔티티 [...]는 범위 규칙에 따라 정상적으로 음영 처리되어야합니다.

그 때문에 clang이 옳다고 말하고 싶습니다.


위에서 설명한 것처럼이 컨텍스트에서 조회는 변환 된 클로저 유형에있는 것처럼 수행되지 않습니다.
Columbo

@Columbo 추론에서 분명 했음에도 불구하고 내가 놓친 줄을 추가하고 있는데, 그것은 clang이 옳다는 것입니다. 재미있는 부분은 대답을하려고하다가 [expr.prim.lambda] / 8을 찾았지만 당신처럼 제대로 사용하지 못했다는 것입니다. 그렇기 때문에 매번 답변을 읽는 것이 즐겁습니다. ;-)
skypjack
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.