다음 코드에서는에 대한 첫 번째 호출 foo
이 모호하므로 컴파일에 실패합니다.
두 번째 +
는 람다 앞에 추가 된 함수 포인터 오버로드로 해결됩니다.
#include <functional>
void foo(std::function<void()> f) { f(); }
void foo(void (*f)()) { f(); }
int main ()
{
foo( [](){} ); // ambiguous
foo( +[](){} ); // not ambiguous (calls the function pointer overload)
}
+
여기서 표기법 은 무엇입니까 ?
답변
+
발현은 +[](){}
단항이며 +
연산자. [expr.unary.op] / 7에서 다음과 같이 정의됩니다.
단항
+
연산자 의 피연산자 는 산술, 범위가 지정되지 않은 열거 또는 포인터 유형을 가져야하며 결과는 인수의 값입니다.
람다는 산술 유형 등이 아니지만 변환 할 수 있습니다.
[expr.prim.lambda] / 3
의 타입 람다 식 […] 고유 이름 불유합 클래스 타입 – 착신 폐쇄 형 – 그 등록을 설명한다.
[expr.prim.lambda] / 6
A에 대한 폐쇄 형 람다 식 아니오 람다 캡처 갖는
public
비virtual
비explicit
const
로 변환 기능 함수 포인터 폐쇄 형의 함수 호출 연산자와 같은 파라미터 및 반환을 가지도록한다. 이 변환 함수에 의해 반환 된 값은 호출 될 때 클로저 유형의 함수 호출 연산자를 호출하는 것과 동일한 효과를 갖는 함수의 주소 여야합니다.
따라서 단항 +
은이 lambda에 대한 함수 포인터 유형으로 변환을 강제합니다 void (*)()
. 따라서 표현식의 유형 +[](){}
은이 함수 포인터 유형 void (*)()
입니다.
두 번째 오버로드 void foo(void (*f)())
는 오버로드 해결 순위에서 정확히 일치하므로 명확하게 선택됩니다 (첫 번째 오버로드는 정확히 일치하지 않음).
람다 [](){}
는의 std::function<void()>
비명 시적 템플릿 ctor 를 통해 로 변환 될 수 있으며 std::function
, 이는 Callable
및 CopyConstructible
요구 사항 을 충족하는 모든 유형을 취합니다 .
람다는 클로저 유형void (*)()
의 변환 함수 를 통해 변환 할 수도 있습니다 (위 참조).
둘 다 사용자 정의 변환 시퀀스이며 순위가 동일합니다. 이것이 첫 번째 예에서 모호성으로 인해 과부하 해결이 실패하는 이유 입니다.
Daniel Krügler의 주장으로 뒷받침 된 Cassio Neri에 따르면,이 단항 +
트릭은 동작을 지정해야합니다. 즉, 신뢰할 수 있습니다 (코멘트의 토론 참조).
그래도 모호함을 피하려면 함수 포인터 유형에 대한 명시 적 캐스트를 사용하는 것이 좋습니다. 그래서 무엇을하고 왜 작동하는지 물어볼 필요가 없습니다.)