2010-07-17 10 views
17

auto_ptr (shared_ptr) tenta di renderlo il più trasparente possibile; idealmente, non dovresti essere in grado di dire una differenza se stai usando un auto_ptr o un vero puntatore a un oggetto. Considerate:Perché il supporto automatico_ptr 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 

Quando si tenta di richiamare una funzione membro attraverso un membro puntatore-a-, c'è una differenza, come auto_ptr ovviamente non implementa op -> *():

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

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

Perché non c'è il supporto per op -> *() in auto_ptr e come lo implementeremo (ho sperimentato per un po 'di tempo, ma alla fine ho rinunciato).

+2

Questa è una domanda molto buona; nessuno dei soliti puntatori intelligenti supporta '-> *'. Nei tuoi esempi, '((* ap). * Memfun)()' è anche valido. –

+1

Perché non usare semplicemente .get()? (Ap.get() -> * memfun)(); – Puppy

risposta

4

attuazione -> * richiederebbe per risolvere il perfetto problema inoltro:

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

operatore -> * avrebbe dovuto restituire un oggetto richiamabile con la stessa lista di parametri come l'oggetto puntatore-a-membro , gestendo correttamente la costanza, la volatilità e i tipi di riferimento. E poi dovrebbe impiegare poteri magici speciali per gestire i parametri di default. Questo è difficile, soggetto a errori, irrisolvibile e richiede troppo tempo di compilazione e poiché i puntatori ai membri sono una caratteristica relativamente popolare del C++, sono generalmente esclusi dalle implementazioni di puntatori intelligenti.

+1

Non è possibile specificare i parametri predefiniti per i puntatori di funzione, in modo da poterlo colpire e anche se difficile non è irrisolvibile. –

+1

Si noti che [# 3] (http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm#s3) nel documento non si applica qui. Attraverso la deduzione sappiamo quali sono i tipi di argomento della funzione membro e possiamo usare una meta-funzione per mapparla al tipo richiesto, riducendo così il numero di overload necessari a 'N'. –

+0

Hai ragione, non me ne sono accorto. –

0

Potrei sbagliarmi, ma penso che non ci sia modo di sovraccaricare operator->* per una funzione puntatore-membro. Questo perché p->*memfun, mentre è valido come parte di un'espressione che lo tratta come un oggetto callable, non è un'espressione valida a sé stante e non ha un tipo. Non esiste quindi un tipo valido per l'operatore da restituire.

Quanto segue funzionerà con un puntatore-membro, ma provando ad utilizzarlo per una funzione pointer-to-member fornisce un errore, "uso non valido della funzione membro non statico", con GCC e un errore interno del compilatore con MSVC.

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

EDIT: come la risposta di Georg sottolinea, è possibile utilizzare boost::bind o simile per creare un insieme di sovraccarichi di funzioni membro fino ad un numero massimo fisso di argomenti, ma non c'è ancora alcun modo per sovraccaricare l'operatore per tutti possibili funzioni membro.

+0

Dovrebbe essere '(* p). * M'. Inoltre, l'operatore ottiene i puntatori funzione membro come secondo argomento, quindi il loro tipo è disponibile e può essere utilizzato per specificare il tipo di ritorno. –

+0

@Georg: grazie, quello era un errore taglia-e-incolla, risolto ora. Non si compila comunque, dal momento che non esiste un tipo che corrisponda a 'MEMBER' se' m' è un puntatore a funzione membro. –

+0

Devi sovraccaricare con '... operator -> * (..., R (Class :: * mfp) (ArgTypes ...))' per quello, vedi la mia risposta. –

8

Come Luther sottolinea la sua non banale implementazione, ma è possibile.

dovete

  1. modelli di utilizzo in modo che il tipo degli argomenti per operator->* può dedurre
  2. prendersi cura di eventuali qualificazioni e molteplici arietà funzione utilizzando sovraccarichi
  3. per puntatori a funzione membro restituire un callabe oggetto che è:
    • associato all'istanza il puntatore intelligente punta a
    • implementa un operator() con una firma equivalente alla funzione di membro

Ignorando le qualificazioni per il momement, ecco come si potrebbe in sostanza guardare (in C++ 0x per evitare repitition manuale):

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

Ma alla fine, perché preoccuparsi quando potremmo semplicemente usare i funtori che legano i puntatori di membri in primo luogo.

mentre io non posso essere sicuro a questo proposito, se si combina la conoscenza che

  • l'implementazione non è banale
  • c'è un'alternativa facile che funziona altrettanto bene ((*p).*m)

... probabilmente non è implementato a causa di un cattivo rapporto del lavoro necessario per i guadagni derivanti da questa funzione.