Подтвердить что ты не робот

Как перегрузить оператор → *?

Я пробовал это, и некоторые вариации на нем:

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;
}

Но он не работает. Проблема в том, что я действительно не знаю, чего ожидать в качестве параметра или что вернуть. Стандарт на этом мне непонятен, и поскольку Google ничего не помог, я думаю, что я не один.

Изменить: Еще одна попытка с С++ 0x: http://ideone.com/lMlyB

4b9b3361

Ответ 1

Возврат 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");
}

Edit2
Здесь - отличный pdf от Скотта Мейерса по этой теме (это единственная хорошая литература о перегрузке operator->*, хотя она и с 1999 года).

Edit
Вот один из них, если ваш компилятор поддерживает вариативные шаблоны: http://ideone.com/B6kRF

Ответ 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. Если кто-то знает способ вернуть лямбду прямо, пожалуйста, дайте мне знать. У меня есть подозрение, что это может быть невозможно из-за того, как определены типы лямбда.

Ответ 3

IIRC, возвращаемый тип operator->* должен быть объектом, который может быть вызван как функция.

Так что вам, возможно, придется переслать указатель на вызов функции-члена объекту, который перегружает operator() (закрытие), что особенно больно, если вы не имеете вариативных шаблонов.

Если у вас есть С++ 0X, у вас есть закрытие и вариативные шаблоны, и ваша проблема может быть решена несколькими строками общего кода.

Если вы этого не сделаете, вам придется использовать макроопределение, чтобы имитировать вариативные шаблоны, и это удовольствие не (даже если некоторые люди думают обратное).