2011-04-07 11 views
16

yo probamos este, y algunas variaciones sobre el mismo:Cómo sobrecargar el operador -> *?

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

Pero doesn't work. El problema es que realmente no sé qué esperar como parámetro o qué devolver. El estándar en esto es incomprensible para mí, y como Google no mencionó nada útil, creo que no estoy solo.

Editar: Otro intento, con C++ 0x: http://ideone.com/lMlyB

Respuesta

8

El retorno de operator->* representa una función en el proceso de ser llamado, con las únicas partes que faltan siendo los parámetros. Por lo tanto, debe devolver un funtor que invoca la función dada en el objeto dado con los parámetros dados:

// 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
Here es una excelente pdf de Scott Meyers en este mismo tema (que es la única buena literatura sobre la sobrecarga operator->*, aunque es de 1999).

Editar
Aquí está uno, si su compilador soporta plantillas variadic: http://ideone.com/B6kRF

+0

Suena aterrador. Tendré que echar un vistazo. – Fozi

+0

@Fozi: Créanme, lo es. Aunque el documento que simplemente no encuentro tiene una implementación completa, y su puntero inteligente solo necesita heredar desde cierta base y todas las funcionalidades 'operator -> *' estarán ahí. : | – Xeo

+0

De hecho, tengo una clase de delegados que podría hacer eso, pero es bastante pesado, por lo que me gustaría evitar devolverlo como temporal. '(* p). * method' es mucho más simple, aunque esperaba poder admitir' -> * 'fácilmente. – Fozi

1

IIRC, el tipo de retorno de operator->* debe ser un objeto que es exigible en función.

Por lo tanto, es posible que tenga que reenviar el puntero a la invocación de función de miembro a un objeto que sobrecarga operator() (un cierre), lo cual es especialmente doloroso si no tiene a mano plantillas variadic.

Si tiene C++ 0X, tiene cierres y plantillas variadic y su problema puede resolverse con unas pocas líneas de código genérico.

Si no lo hace, tendrá que usar macro trickery para simular plantillas variadic, y esto es no divertido de hacer (incluso si algunas personas piensan lo contrario).

+0

Si voy por la ruta C++ 0x, ¿no debería el método p -> * devolver el objeto correcto? ¿Un tipo de devolución automática no sería suficiente? http://ideone.com/lMlyB Otra vez, estoy atascado. – Fozi

+0

@Fozi: Muy buen punto. Tal vez. No soy C++ 0x lo suficientemente inteligente, jefe, diría que sí, que haría el truco. O tal vez 'decltype' también funcionaría. –

2

volví a este problema y he encontrado una solución sencilla:

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

La ejecución del caso de prueba completa se puede encontrar here.

Lo único que no me gusta en esta solución es el uso de std::function. Si alguien sabe cómo devolver el lambda directamente, házmelo saber. Tengo la corazonada de que podría no ser posible debido a la forma en que se definen los tipos lambda.

+0

Simplemente no especifique un tipo de devolución. Como solo tiene una sola declaración de devolución, el tipo de devolución se deduce automáticamente al tipo interno de lambda. De lo contrario, +1 para la solución. :) – Xeo

+0

@Xeo Eso solo funciona para lambdas, pero debo especificar el tipo de retorno para el operador '-> *', que es un 'decltype ()', pero el lambda no se puede especificar allí porque está usando 'this 'y además cada instancia lambda tiene su propio tipo. :( – Fozi

+0

Oh, claro, pasé por alto algo .((Por desgracia, no creo que salgas de esa 'función de std ::. – Xeo

Cuestiones relacionadas