1

我遇到了一个用例,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这种用例的方式实现,就像使用手卷包装器一样工作?

4

1 回答 1

5

有可能,是的,但这不是 C++ 标准指定的mem_fn.

该标准说,在 [func.require] 中定义的foo1(a)调用为:INVOKE(&B::foo, a)

定义INVOKE (f, t1, t2, ..., tN)如下:
(t1.*f)(t2, ..., tN)whenf是指向类的成员函数的指针,T并且t1是类型对象T或类型对象的T引用或派生类型对象的引用T
((*t1).*f)(t2, ..., tN)当 f 是指向类的成员函数的指针T并且t1不是上一项中描述的类型之一时;
— ...

您的案例不符合第一个项目符号的条件,因为a它不是 class 的对象B,也不是对 aB或派生自 的类的引用B,因此第二个项目符号适用,因此它等同于((*a).*f)()which 无效。

它以这种方式定义以允许使用智能指针,例如

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

INVOKE(也被bind,function和创建调用包装器的库的其他部分使用)的定义async意味着当调用包装的指向成员的指针时,如果第一个参数t1不是 aT那么它被假定为某种指针并被取消引用。这意味着它适用于std::shared_ptrandstd::unique_ptr但也适用于std::mem_fn一无所知的类型,例如boost::shared_ptrand MyVeryOwnSmartPtr

为了使您的代码正常工作,可以添加额外的情况来处理 when t1is not aT或 type 派生自T,但is_convertible<T>::value为 true ,并调用T(t1).*f)(),但这会使规范复杂化,并且在某些情况下可能会产生不良后果。

您的“包装器”将强制对其参数进行隐式转换,但它无法处理 type 的智能指针或右值B,它们都受mem_fn. 如果您有特定情况想要将A对象转换B为调用函数,那么就这样做,通用mem_fn模板不适合,但它更灵活和通用,并且适用于许多其他情况。

(注意 的定义INVOKE实际上是有缺陷的,因为它std::reference_wrapper以与取消引用您的论点相同的方式取消引用对象a。我在http://cplusplus.github.com/LWG/lwg-active.html#2219提出了修复,但是这不会影响您的示例。)

于 2013-02-08T09:41:26.307 回答