Как перегрузить оператор → *?
Я пробовал это, и некоторые вариации на нем:
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
Ответы
Ответ 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, у вас есть закрытие и вариативные шаблоны, и ваша проблема может быть решена несколькими строками общего кода.
Если вы этого не сделаете, вам придется использовать макроопределение, чтобы имитировать вариативные шаблоны, и это удовольствие не (даже если некоторые люди думают обратное).