我遇到过一个用例,其中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
,使得这个用例就像手工卷制的包装一样工作?
答案 0 :(得分:5)
有可能,是的,但不是C ++标准如何指定mem_fn
。
标准规定foo1(a)
调用INVOKE(&B::foo, a)
,其中[func.require]中将其定义为:
如下定义
INVOKE (f, t1, t2, ..., tN)
:
-(t1.*f)(t2, ..., tN)
当f
是指向类T
的成员函数的指针时,t1
是T
类型的对象或对象的引用键入T
或对从T
派生的类型的对象的引用;
-((*t1).*f)(t2, ..., tN)
当f是指向类T
的成员函数的指针时,t1
不是上一项中描述的类型之一;
- ......
您的案例无法满足第一个项目符号的条件,因为a
不是类B
的对象,也不是对B
或B
派生的类的引用1}},所以第二个项目符号适用,因此相当于((*a).*f)()
无效。
它以这种方式定义,允许使用智能指针,例如
auto foo1 = std::mem_fn(B::foo);
auto p = std::make_shared<B>();
foo1(p);
INVOKE
的定义(bind
,function
,async
以及创建调用的库的其他部分也使用了 t1
的定义包装器)意味着当调用包装指向成员的指针时,如果第一个参数T
不是std::shared_ptr
,则假定它是某种指针并被取消引用。这意味着它适用于std::unique_ptr
和std::mem_fn
,但也适用于boost::shared_ptr
一无所知的类型,例如MyVeryOwnSmartPtr
和t1
。
为了使代码正常工作,可以添加额外的案例来处理T
不是T
或来自is_convertible<T>::value
的类型,但是T(t1).*f)()
是是的,并调用B
,但这会使规范复杂化,并且在某些情况下可能会产生不良后果。
您的“包装器”将强制隐式转换其参数,但它无法处理mem_fn
支持的A
类型的智能指针或右值。如果您有特定情况要将B
对象转换为mem_fn
以调用该函数,那么只需执行此操作,通用INVOKE
模板不适用,但它更多灵活和通用,适用于许多其他情况。
(注意 std::reference_wrapper
的定义实际上是有缺陷的,因为它取消引用a
个对象的方式与取消引用{{1}}参数的方式相同。我是提议修复http://cplusplus.github.com/LWG/lwg-active.html#2219,但这不会影响您的示例。)