2010-02-10 2 views
9

Per verificare e visualizzare il risultato di alcune funzioni della mia libreria, sto creando un insieme di funzioni a portata di mano.Sovraccarico dell'operatore di conversione di tipo globale

devo una funzione execute che assomiglia:

template <typename R, typename I> 
std::string execute(const std::string& func_name, R(*func_ptr)(const I&), const I& func_input); 

Si chiama la funzione, e visualizzare i risultati e gli argomenti in una stringa formattata che posso inviare a std::cout.

Il problema è che alcune delle mie funzioni non restituiscono risultati convertibili in stringa. Ho pensato che avrei potuto semplicemente sovraccaricare il globale ::operator std::string con qualcosa di simile:

template <typename T> 
operator std::string(const std::vector<T>& v); 

Ma GCC si lamenta:

error: 'operator std::string(const std::vector<T, std::allocator<_CharT> >&)' must be a nonstatic member function 

Beh, il problema, naturalmente, è che non posso aggiungere soci gestori std::vector, e anche per le mie lezioni, non voglio inquinarle con "per testare" gli operatori di conversione.

Immagino di poter aggiungere uno strato di riferimento indiretto e utilizzare una funzione anziché un operatore di conversione, ma non sarebbe la soluzione più estetica. Potrei anche sovraccaricare ::operator << per std::ostream e utilizzare uno std::ostringstream, ma anche questa non è la soluzione più pulita.

Mi chiedevo se l'operatore di conversione globale non fosse realmente sovraccarico e, in tal caso, perché.

risposta

10

Gli operatori di conversione (operatori cast) devono essere membri della classe convertibile che produce il tipo convertito. Come operatori di assegnazione, devono essere funzioni membro, come il tuo compilatore ti sta dicendo.

A seconda di quanto sforzo si vuole mettere nella parte di debug di esso, si potrebbe provare a utilizzare metaprogramming per inoltrare il metodo di esecuzione a diverse implementazioni effettive, fornendo quelle specifiche per i contenitori che stamperanno i contenuti.

Perché non vuoi fornire operator<< per i tuoi tipi? Penso che sia in realtà la soluzione idiomatica.A differenza di altri linguaggi, i metodi che si convertono in stringono per produrre risultati stampabili, in C++ il modo idiomatico fornisce operator<< e quindi utilizza stringstreams (o boost::lexical_cast o una soluzione simile) per convertire in stringhe basate sull'implementazione operator<<. Esiste una semplice classe di utilità here per creare un string da elementi che sostituiscono operator<< se si desidera utilizzarlo per un punto di partenza.

1

Non esiste un operatore di conversione globale definito dall'utente. È necessario controllare il tipo di destinazione (nel qual caso un costruttore di parametri non esplicito è l'operatore di conversione) o il tipo di origine (nel qual caso è necessario sovraccaricare l'operatore membro target()).

4

Mi chiedevo se l'operatore di conversione globale non è davvero sovraccarico e, in tal caso, perché.

No, non esiste una cosa del genere. Le funzioni di conversione devono essere membri di una classe. Se non fosse così, renderebbe la risoluzione del sovraccarico un problema particolarmente fastidioso per il compilatore introducendo ambiguità.

+1

Ma molti operatori sono disponibili come globale o membro, perché non questo? Ad esempio, se c'è un operatore globale e un membro <<, il compilatore si lamenta per una chiamata ambigua, potrebbe fare la stessa cosa con le conversioni. – NewbiZ

+2

Le funzioni di conversione sono funzioni membro speciali (così come Ctor, Dtor, Op = e Copia-Costruttore) poiché partecipano alla creazione di conversioni/oggetti. Vedi 12.3. – dirkgently

0

Una funzione di conversione deve essere una funzione membro. La funzione potrebbe non specificare un tipo di ritorno e l'elenco dei parametri deve essere vuoto. Dovrebbero essere usati con parsimonia e ci dovrebbe essere un chiaro percorso di conversione da un tipo all'altro. Altrimenti possono portare a risultati imprevisti e errori misteriosi.

0

Sfortunatamente non esiste un operatore di casting globale. Sorprendentemente. Ma i modelli sono tuoi amici.

A volte non si vuole esporre il casting all'interfaccia put si vorrebbe mantenere questo anonimo solo per una specifica implementazione. Solitamente aggiungo un metodo template as() alla classe che può anche eseguire controlli di tipo nel cast, ecc. E consente di gestire il modo in cui si desidera implementare il cast (ad esempio dinamico, condiviso, ref, ecc.).

Qualcosa di simile a questo:

template< class EvtT >const EvtT& as() const throw(std::exception) 
    { 
     const EvtT* userData = static_cast<const EvtT*>(m_UserData); 
     ASSERT(userData, "Fatal error! No platform specific user data in input event!"); 
     return *userData; 
    } 

m_UserData è un tipo anonimo, che solo l'attuazione conosce. Anche se questo è un cast non tipizzato (non uso i ceck di tipo qui) questo potrebbe essere sostituito da un dynamic_cast e da eccezioni di casting appropriate.

L'applicazione fa semplicemente questo:

unsigned char GetRawKey(const InputEvent& ie) 
    { 
     const UserEvent& ue = ie.as<const UserEvent>(); 
     ...