2010-07-17 26 views
17

auto_ptr (shared_ptr también) intente que su uso sea lo más transparente posible; es decir, idealmente, no debería ser capaz de diferenciar si está utilizando un auto_ptr o un puntero real a un objeto. Considere:¿Por qué auto_ptr no admite op -> *()

class MyClass 
{ 
public: 
    void foo() { } 
}; 

MyClass* p = new MyClass; 
auto_ptr<MyClass> ap(new MyClassp); 

p->foo();  // No notational difference in using real 
ap->foo();  // pointers and auto_ptrs 

Cuando intenta invocar una función miembro a través de un puntero a miembro-, hay una diferencia, como auto_ptr obviamente no implementa op -> *():

void (MyClass::*memfun)() = &MyClass::foo; 

(p->*memfun)();   // OK 
(ap->*memfun)();  // Error op->*() missing 
(ap.get()->*memfun)(); // OK 

¿Por qué no hay soporte para op -> *() en auto_ptr y cómo se implementaría (he experimentado durante un tiempo, pero finalmente me rendí).

+2

Esta es una muy buena pregunta; ninguno de los punteros inteligentes usuales admiten '-> *'. En sus ejemplos, '((* ap). * Memfun)()' también es válido. –

+1

¿Por qué no usar simplemente .get()? (ap.get() -> * memfun)(); – Puppy

Respuesta

4

implementar -> * requeriría para resolver el problema de reenvío perfecto:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm

operador -> * tendría que devolver un objeto invocable con la misma lista de parámetros como el objeto-puntero a miembro , manejando correctamente los tipos de const, volatilidad y referencia. Y luego tendría que emplear poderes mágicos especiales para manejar los parámetros predeterminados. Esto es difícil, propenso a errores, no se puede resolver y consume demasiado tiempo de compilación, y como el puntero a los miembros es una característica comparativamente marginalmente popular de C++, por lo general quedan fuera de las implementaciones de punteros inteligentes.

+1

No puede especificar los parámetros predeterminados para los punteros de función, por lo que puede atacar eso, y aunque es difícil, no es irresoluble. –

+1

Tenga en cuenta que [# 3] (http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm#s3) en el documento no se aplica aquí. Mediante la deducción sabemos cuáles son los tipos de argumento de la función miembro y podemos usar una metafunción para asignarla al tipo requerido, reduciendo así el número de sobrecargas requeridas a 'N'. –

+0

Tienes razón, no me di cuenta de esto. –

0

Puedo estar equivocado, pero creo que no hay forma de sobrecargar operator->* para una función de apuntador a miembro. Esto es porque p->*memfun, aunque es válido como parte de una expresión que lo trata como un objeto invocable, no es una expresión válida por derecho propio, y no tiene un tipo. Por lo tanto, no hay un tipo válido para que el operador regrese.

Lo siguiente funcionará para un puntero a miembro, pero al tratar de usarlo para una función de puntero a miembro aparece un error, "uso no válido de la función miembro no estático", con GCC, y un error del compilador interno con MSVC.

template <class CLASS, typename MEMBER> 
MEMBER& operator->*(std::auto_ptr<CLASS>& p, MEMBER CLASS::*m) 
{ 
    return (*p).*m; 
} 

EDIT: como la respuesta de Georg señala, puede utilizar boost::bind o similar para crear un conjunto de sobrecargas para las funciones miembro, hasta un número máximo fijo de argumentos, pero todavía no hay manera de sobrecargar el operador para todos posibles funciones de miembros.

+0

Eso debería ser '(* p). * M'. Además, el operador obtiene los punteros de función miembro como su segundo argumento, por lo que su tipo está disponible y se puede usar para especificar el tipo de devolución. –

+0

@Georg: gracias, eso fue un error de cortar y pegar, corregido ahora. Sin embargo, aún no compila, ya que no hay ningún tipo para que coincida con 'MIEMBRO' si' m' es un puntero de función miembro. –

+0

Tiene que sobrecargar con '... operator -> * (..., R (Class :: * mfp) (ArgTypes ...))' para eso, vea mi respuesta. –

8

Como señala Luther, no es trivial implementarlo, pero es posible.

usted tiene que

  1. usar plantillas por lo que el tipo de los argumentos a operator->* puede deducir
  2. cuidar de posibles calificadores y múltiples arities función utilizando sobrecargas
  3. para los punteros de función miembro devolver un callabe objeto que es:
    • vinculado a la instancia que el puntero inteligente apunta a
    • implementa un operator() con una firma equivalente a la función miembro

Haciendo caso omiso de los calificadores para la momement, aquí es la forma en que básicamente podría buscar (usando C++ 0x para evitar Repetición del manual):

// pointer to data member: 

template<class T, class D> 
D& operator->*(std::auto_ptr<T>& p, D T::*mp) { 
    return (*p).*mp; 
} 

// pointer to member function: 

template<class T, class R, class... Args> struct Callable { 
    typedef R (T::*MFP)(Args...); 
    MFP mfp; 
    T& instance; 

    Callable(T t, MFP mfp) : instance(t), mfp(mfp) {} 

    R operator()(Args... a) { 
     return (instance.*mfp)(a...); 
    } 
}; 

template<class T, class R, class... Args> 
Callable<T, R, Args...> 
operator->*(std::auto_ptr<T>& p, R (T::*mfp)(Args...)) { 
    return Callable<T, R, Args...>(*p, mfp); 
} 

Pero al final, ¿para qué molestarse cuando podríamos usar funtores que unen punteros de miembro en primer lugar.

Aunque no puedo estar seguro de que, si se combina el conocimiento de que

  • la implementación no es trivial
  • no es una alternativa fácil que funciona igual de bien ((*p).*m)

... probablemente no se implemente debido a una mala relación entre el trabajo necesario y las ganancias resultantes de esta característica.

Cuestiones relacionadas