如何重载 ->* 运算符?

16

我尝试了这个方法,以及一些变体:

template<class T>
class Ptr {
public:
    Ptr(T* ptr) : p(ptr) {}
    ~Ptr() { if(p) delete p; }

    template<class Method>
    Method operator ->* (Method method)
    {
        return p->*method;
    }

private:
    T *p;
};

class Foo {
public:
    void foo(int) {}
    int bar() { return 3; }
};

int main() {
    Ptr<Foo> p(new Foo());

    void (Foo::*method)(int) = &Foo::foo;
    int (Foo::*method2)() = &Foo::bar;

    (p->*method)(5);
    (p->*method2)();

    return 0;
}

但是它不起作用。问题在于我不知道期望什么参数或返回什么。我对此标准无法理解,而且由于谷歌没有提供有用的信息,我猜我不是唯一一个这样想的人。

编辑: 另一种尝试,使用C++0x:http://ideone.com/lMlyB

3个回答

8
operator->*的返回值表示正在被调用的函数,唯一缺失的部分是参数。因此,你必须返回一个函数对象,它会在给定对象上使用给定参数调用指定函数:
// PTMF = pointer to member function
template<class Obj>
struct PTMF_Object{
  typedef int (Obj::*ptmf)(double,std::string); // example signature

  PTMF_Object(Obj* obj, ptmf func)
    : obj_(obj)
    , func_(func)
  {}

  int operator()(double d, std::string str){
    return (obj_->*func_)(d,str);
  }

  Obj* obj_;
  ptmf func_;
};

template<class T>
struct SmartPtr{
  // ...

  PTMF_Object<T> operator->*(PTMF_Object<T>::ptmf func){
    return PTMF_Object<T>(p, func);
  }
  // ...
};

int main(){
  SmartPtr<Foo> pf(new Foo());
  typedef int (Foo::*Foo_ptmf)(double,std::string);
  Foo_ptmf method = &Foo::bar;

  (pf->*method)(5.4, "oh hi");
}

编辑2
这里有一篇来自Scott Meyers的优秀pdf,讲述了这个话题(即使它是来自1999年,但是这是关于重载operator->*唯一好的文献)。

编辑
如果您的编译器支持可变参数模板,则可以使用此代码:http://ideone.com/B6kRF


听起来有点吓人。我得去看看。 - Fozi
@Fozi:相信我,它是可以的。虽然我找不到的那篇论文中有完整的实现,但你的智能指针只需要继承自某个基类,所有 operator->* 的功能都会在那里。 :| - Xeo
我实际上有一个Delegate类,可能能够做到这一点,但它非常笨重,所以我想避免将其作为临时返回。(*p).*method要简单得多,尽管我希望能够轻松支持->* - Fozi
这看起来很有前途。问题是我们还没有使用c++0x。谢谢。 - Fozi
@Fozi:那就试试更新的版本,你只需要将其扩展到参数计数>=3即可。 :) - Xeo
显示剩余2条评论

2

我回到这个问题,发现了一个简单的解决方案:

template<class Ret, class... Args>
auto operator ->* (Ret (T::*method)(Args...)) -> std::function<Ret(Args...)>
{
  return [this, method](Args&&... args) -> Ret {
    return (this->p->*method)(std::forward<Args>(args)...);
  };
}

完整测试用例的实现可以在这里找到。

我唯一不喜欢这个解决方案的地方是使用了std::function。如果有人知道直接返回lambda的方法,请告诉我。虽然我有一种预感,可能因为lambda类型的定义方式而不可能实现。


只需不指定返回类型。由于您只有一个return语句,返回类型会自动推断为内部lambda的类型。否则,对于此解决方案加1分。 :) - Xeo
@Xeo 这仅适用于 lambda,但我必须为运算符 ->* 指定返回类型,该类型是 decltype (<my lambda>),但无法在此指定 lambda,因为它正在使用 this,而且每个 lambda 实例都具有其自己的类型。 :( - Fozi
哦,对了,我忽略了一些东西。:( 不幸的是,我认为你无法避免使用 std::function - Xeo

1

IIRC,operator->* 的返回类型应该是一个可被调用为函数的对象。

因此,您可能需要将成员函数指针调用转发到重载了 operator()(闭包)的对象上,如果没有可变参数模板,则这尤其麻烦。

如果您有 C++0X,则具有闭包和可变参数模板,并且可以使用几行通用代码解决问题。

如果没有,则必须使用宏技巧来模拟可变参数模板,而且这并不好玩(即使有些人认为相反)。


如果我选择C++0x路径,p->*method不应该已经返回正确的对象吗?自动返回类型不足以解决问题吗?http://ideone.com/lMlyB 我又卡住了。 - Fozi
@Fozi:非常好的观点。也许吧。虽然我不够熟悉C++0x,但我想是的,那应该可以解决问题。或者也许使用decltype也行。 - Alexandre C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接