2013-02-08 2 views
1

std::mem_fn은 핸드 롤 랩퍼 기능이 수행 할 수없는 사용 사례가 있습니다.std :: mem_fn의 불완전 성이 핸드 롤 기능과 비교

#include <functional> 

struct A 
{ 
}; 

struct B 
{ 
    B(A); // implicit conversion from A to B 
    void foo() const; 
}; 


auto foo1 = std::mem_fn(&B::foo);  // std::mem_fn 

void foo2(const B& b) { b.foo(); } // hand-rolled wrapper 

int main() 
{ 
    A a; 
    foo1(a); // doesn't work 
    foo2(a); // works fine 
} 

foo1은 호출에 대한 컴파일러 오류가 (GCC 4.8) 다음이다 : 그것은 래퍼 기능이 방법의 수업이 아닌 무언가에 사용하지만, 그것에 유형 암시 적으로 변환 할 때 온다 :

In file included from test.cpp:1:0: 
functional: In instantiation of '_Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)const>::_M_call(_Tp&, const volatile void*, _ArgTypes ...) const [with _Tp = A; _Res = void; _Class = B; _ArgTypes = {}]': 
functional:608:42: required from '_Res std::_Mem_fn<_Res (_Class::*)(_ArgTypes ...)const>::operator()(_Tp&, _ArgTypes ...) const [with _Tp = A; _Res = void; _Class = B; _ArgTypes = {}]' 
test.cpp:21:11: required from here 
functional:586:13: error: no match for 'operator*' (operand type is 'A') 
    { return ((*__ptr).*__pmf)(std::forward<_ArgTypes>(__args)...); } 
      ^

은이 유스 케이스는 손으로 압연 래퍼 않는 것처럼 작동하는 방식으로 std::mem_fn을 구현하는 것이 가능했을 것이다?

답변

5

그렇습니다. 그렇지만 C++ 표준이 mem_fn을 지정하는 방식이 아닙니다.

표준은 foo1(a)가이 같이 func.require]에 정의되어 INVOKE(&B::foo, a) 호출 있다고 다음과 같이

INVOKE (f, t1, t2, ..., tN)을 정의
- (t1.*f)(t2, ..., tN)f 클래스의 멤버 함수에의 포인터 인 경우를 Tt1T 유형의 객체이거나 T 유형의 객체에 대한 참조이거나 T에서 파생 된 유형의 객체에 대한 참조입니다.
- ((*t1).*f)(t2, ..., tN) f가 클래스 T의 멤버 함수에 대한 포인터이고 t1이 이전 항목에서 설명한 유형 중 하나가 아닌 경우;
- ...

케이스가 a 클래스 B의 목적, 아니고 B 참조 또는 B에서 파생 된 클래스가 없기 때문에, 제 탄환의 조건을 충족하지 못하는

그래서 제 총알이 적용되므로 유효하지 않은 ((*a).*f)()과 같습니다.

이렇게하면 스마트 포인터를 사용할 수 있습니다.

auto foo1 = std::mem_fn(B::foo); 
auto p = std::make_shared<B>(); 
foo1(p); 

은 (또한 bind 의해 사용된다 function, async 및 통화 래퍼 생성 라이브러리의 다른 부분) INVOKE의 정의는 제 경우 래핑 포인터 투 부재를 호출 할 때 것을 의미 인수 t1T이 아니며 일종의 포인터로 간주되어 참조 해제됩니다. 즉, std::shared_ptrstd::unique_ptr과 함께 작동하지만 및 MyVeryOwnSmartPtr과 같이 std::mem_fn이 전혀 알지 못하는 유형으로도 작동합니다.

t1가없는 경우 처리하기 위해 별도의 케이스를 추가 할 수있을 것입니다 코드를 작동하게하는 T 또는 T에서 파생 된 유형,하지만 is_convertible<T>::value 사실이며, T(t1).*f)()를 호출 할 수 있지만,이 사양을 복잡하게 수도 것 어떤 경우에는 바람직하지 않은 결과를 초래합니다.

"래퍼"는 인수의 암시 적 변환을 강제로 수행하지만 B의 스마트 포인터 또는 rvalues는 모두 mem_fn으로 지원되지 않습니다.특정 케이스에서 A 오브젝트를 B으로 변환하여 함수를 호출하려는 경우 일반 mem_fn 템플릿은 적합하지 않지만 더 유연하고 일반적인 것으로 다른 많은 상황에서 작동합니다.

(그것은 내가 http://cplusplus.github.com/LWG/lwg-active.html#2219에서 수정을 제안했습니다. 당신의 a 인수를 역 참조로이 같은 방법으로 std::reference_wrapper 객체를 역 참조,하지만 당신의 예에 영향을주지 않기 때문에주의가 INVOKE의 정의가 실제로 결함이있는 것입니다.)