HC4*_*ica 1 c++ mem-fun implicit-conversion c++11
我遇到过一个用例,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
}
Run Code Online (Sandbox Code Playgroud)
调用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)...); }
^
Run Code Online (Sandbox Code Playgroud)
是否有可能以std::mem_fn这样的方式实现这个用例就像手工卷制的包装一样工作?
这是可能的,是的,但这不是C++标准规定的方式mem_fn.
该标准表示foo1(a)调用INVOKE(&B::foo, a)[func.require]中定义的位置为:
定义
INVOKE (f, t1, t2, ..., tN)如下:
-(t1.*f)(t2, ..., tN)whenf是指向类的成员函数的指针T,t1是类型T的对象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);
Run Code Online (Sandbox Code Playgroud)
的定义INVOKE(其也使用bind,function,async和创建呼叫包装的库的其他部分),表示调用,当包裹指针到构件,如果第一自变量t1不是T那么它被认为是某种指针和被取消引用.这意味着它可以使用std::shared_ptr,std::unique_ptr但也适用于那些std::mem_fn一无所知的类型,例如boost::shared_ptr和MyVeryOwnSmartPtr.
为了使你的代码工作,可以添加额外的案例来处理何时t1不是a T或派生的类型T,但是is_convertible<T>::value是真的,并且要调用T(t1).*f)(),但这会使规范复杂化并且在某些情况下可能会产生不良后果.
你的"包装器"将强制其参数的隐式转换,但它不能处理智能指针或类型的右值B,它们都受到支持mem_fn.如果你有一个特定的情况,你想要转换A对象来B调用函数,那么只是这样做,通用mem_fn模板不适合,但它更灵活和通用,并在许多其他情况下工作.
(注意,定义INVOKE实际上是有缺陷的,因为它取消引用std::reference_wrapper对象的方式与取消引用你的a论点的方式相同.我在http://cplusplus.github.com/LWG/lwg-active.html#2219提出修复,但是这不会影响你的例子.)