[c++11] +를 사용하여 람다에 대한 함수 포인터 및 std :: function에 대한 모호한 오버로드 해결

다음 코드에서는에 대한 첫 번째 호출 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에 대한 폐쇄 형 람다 식 아니오 람다 캡처 갖는 publicvirtualexplicit const로 변환 기능 함수 포인터 폐쇄 형의 함수 호출 연산자와 같은 파라미터 및 반환을 가지도록한다. 이 변환 함수에 의해 반환 된 값은 호출 될 때 클로저 유형의 함수 호출 연산자를 호출하는 것과 동일한 효과를 갖는 함수의 주소 여야합니다.

따라서 단항 +은이 lambda에 대한 함수 포인터 유형으로 변환을 강제합니다 void (*)(). 따라서 표현식의 유형 +[](){}은이 함수 포인터 유형 void (*)()입니다.

두 번째 오버로드 void foo(void (*f)())는 오버로드 해결 순위에서 정확히 일치하므로 명확하게 선택됩니다 (첫 번째 오버로드는 정확히 일치하지 않음).


람다 [](){}는의 std::function<void()>비명 시적 템플릿 ctor 를 통해 로 변환 될 수 있으며 std::function, 이는 CallableCopyConstructible요구 사항 을 충족하는 모든 유형을 취합니다 .

람다는 클로저 유형void (*)() 의 변환 함수 를 통해 변환 할 수도 있습니다 (위 참조).

둘 다 사용자 정의 변환 시퀀스이며 순위가 동일합니다. 이것이 첫 번째 예에서 모호성으로 인해 과부하 해결이 실패하는 이유 입니다.


Daniel Krügler의 주장으로 뒷받침 된 Cassio Neri에 따르면,이 단항 +트릭은 동작을 지정해야합니다. 즉, 신뢰할 수 있습니다 (코멘트의 토론 참조).

그래도 모호함을 피하려면 함수 포인터 유형에 대한 명시 적 캐스트를 사용하는 것이 좋습니다. 그래서 무엇을하고 왜 작동하는지 물어볼 필요가 없습니다.)


답변