C ++ 11에서 "auto"로 추론 할 때 람다 유형은 무엇입니까?


142

람다의 유형은 함수 포인터라는 인식이있었습니다. 다음 테스트를 수행했을 때 잘못된 것으로 나타났습니다 ( demo ).

#define LAMBDA [] (int i) -> long { return 0; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok
  assert(typeid(pFptr) == typeid(pAuto));  // assertion fails !
}

위의 코드에 포인트가 없습니까? 그렇지 않으면 키워드로 typeof추론 할 때 람다 식은 auto무엇입니까?


8
"람다의 유형은 함수 포인터입니다"– 이것은 비효율적이며 람다의 요점을 모두 놓칩니다.
Konrad Rudolph

답변:


145

람다 식의 유형이 지정되지 않았습니다.

그러나 그들은 일반적으로 functors에 대한 단순한 구문 설탕입니다. 람다는 functor로 직접 변환됩니다. 내부의 모든 []것은 생성자 매개 변수 및 functor 객체의 멤버로 바뀌며 내부의 매개 변수 ()는 functor 's의 매개 변수로 바뀝니다 operator().

변수를 캡처하지 않는 람다는 (의 안에는 없음 []) 함수 포인터 로 변환 할 수 있습니다 (MSVC2010은 컴파일러 인 경우 이것을 지원하지 않지만이 변환은 표준의 일부입니다).

그러나 람다의 실제 유형은 함수 포인터가 아닙니다. 지정되지 않은 functor 유형입니다.


1
MSVC2010은 함수 포인터로의 변환을 지원하지 않지만 MSVC11은 지원합니다. blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
KindDragon

18
"functors를위한 단순한 구문 설탕"+1 이것을 기억함으로써 많은 잠재적 혼란을 피할 수 있습니다.
Ben

4
와 펑 아무것도이다 operator()기본적으로 stackoverflow.com/questions/356950/c-functors-and-their-uses
TankorSmash

107

함수 호출 연산자를 오버로드하는 고유 한 명명되지 않은 구조입니다. 람다의 모든 인스턴스는 새로운 유형을 도입합니다.

캡처되지 않는 람다의 특수한 경우 구조는 함수 포인터로 암시 적으로 변환됩니다.


2
좋은 대답입니다. 내 것보다 훨씬 더 정확합니다. +1 :)
jalf

4
unicity 부분의 경우 +1, 처음에는 매우 놀랍고 주목할만한 가치가 있습니다.
Matthieu M.

실제로 중요하지는 않지만 형식의 이름이 실제로 지정되지 않았습니까? 아니면 컴파일 시간까지 이름이 지정되지 않았습니까? IOW, RTTI를 사용하여 컴파일러가 결정한 이름을 찾을 수 있습니까?
Ben

3
@Ben은 이름이 지정되지 않았으며 C ++ 언어와 관련하여 "컴파일러가 결정하는 이름"과 같은 것은 없습니다. 결과 type_info::name()는 구현에 따라 정의되므로 아무 것도 반환 할 수 있습니다. 실제로 컴파일러는 링커를 위해 유형의 이름을 지정합니다.
avakar

1
이 질문을 할 때 최근에, 나는 보통 람다의 유형이 있다고 이름을 컴파일러는 그것을 알고, 그냥 말로 표현할 수없는입니다.
Andre Kostur

24

[C++11: 5.1.2/3]: 의 타입 람다 식 (또한 밀폐 객체의 유형) 의 고유 이름 불유합 클래스 타입 - 착신 폐쇄 형 그 특성 설명한다 -. 이 클래스 유형은 집계 (8.5.1)가 아닙니다. 클로저 유형은 해당 lambda-expression 을 포함하는 가장 작은 블록 범위, 클래스 범위 또는 네임 스페이스 범위에서 선언됩니다 . [..]

이 절에서는이 유형의 다양한 속성을 나열합니다. 주요 내용은 다음과 같습니다.

[C++11: 5.1.2/5]:람다 식 의 클로저 형식 에는 inline매개 변수와 반환 형식이 각각 람다 식매개 변수 선언 절후행 반환 형식으로 설명되는 공용 함수 호출 연산자 (13.5.4)가 있습니다. [..]

[C++11: 5.1.2/6]:A의 폐쇄 형 람다 표현 아니오 람다 캡처는 폐쇄 형의 함수 호출 연산자와 같은 매개 변수와 리턴 유형을 가진 함수 포인터에 공개되지 않은 가상이 아닌 명시 적 const를 변환 기능이 있습니다. 이 변환 함수에 의해 리턴 된 값은 호출 될 때 클로저 유형의 함수 호출 연산자를 호출하는 것과 동일한 효과를 갖는 함수의 주소입니다.

마지막 구절의 결과는 당신이 변환을 사용하는 경우, 당신은 할당 할 수있을 것,이다 LAMBDApFptr.


3
#include <iostream>
#include <typeinfo>

#define LAMBDA [] (int i)->long { return 0l; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok

  std::cout<<typeid( *pAuto ).name() << std::endl;
  std::cout<<typeid( *pFptr ).name() << std::endl;

  std::cout<<typeid( pAuto ).name() << std::endl;
  std::cout<<typeid( pFptr ).name() << std::endl;
}

함수 유형은 실제로 동일하지만 람다는 새 유형을 도입합니다 (예 : functor).


이미이 길을 가고 있다면 CXXABI 언글 글링 접근법을 권장 합니다. 대신, 나는 보통 __PRETTY_FUNCTION__에서 template<class T> const char* pretty(T && t) { return __PRETTY_FUNCTION__; }와 같이를 사용 하고 혼잡 해지기 시작하면 추가를 제거합니다. 템플릿 대체에 표시된 단계를 선호합니다. 누락 된 경우 __PRETTY_FUNCTION__MSVC 등의 대안이 있지만 결과는 항상 CXXABI가 필요한 동일한 이유로 컴파일러에 따라 다릅니다.
John P

1

또한 람다는 함수 포인터로 변환 할 수 있습니다. 그러나 typeid <>는 람다와 일반 함수 포인터에 따라 다르지 않은 객체를 반환합니다. 따라서 typeid <> 테스트는 유효한 가정이 아닙니다. 일반적으로 C ++ 11은 유형 지정에 대해 걱정하지 않으려 고합니다. 주어진 유형이 대상 유형으로 변환 가능한지 여부는 중요합니다.


공평하지만 인쇄 유형은 올바른 유형에 도달하기 위해 먼 길을갑니다. 유형이 변환 가능하지만 다른 제약 조건을 충족시키지 않는 경우를 잡는 것은 말할 것도 없습니다. (저는 가능한 한 항상 "강화"제약을 추구하지만, 그렇게하려는 누군가는 개발하는 동안 그들의 작업을 보여줄 더 많은 이유가 있습니다.)
John P

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.