11

consideri il seguente esempio minimale:Ordine di risoluzione di sovraccarico dell'operatore che coinvolge provvisori

#include <iostream> 

using namespace std; 

class myostream : public ostream { 
    public: 
     myostream(ostream const &other) : 
      ostream(other.rdbuf()) 
     { } 
}; 

int main() { 
    cout << "hello world" << endl; 

    myostream s(cout); 
    s << "hello world" << endl; 

    myostream(cout) << "hello world" << endl; 
} 

L'uscita, sia sul g ++ e Visual C++, è

hello world 
hello world 
0x4012a4 

La versione che scrive ad un oggetto temporaneo , myostream(cout), sembra preferire l'operatore membro ostream::operator<<(void *), anziché l'operatore libero operator<<(ostream &, char *). Sembra fare la differenza se l'oggetto ha o meno un nome.

Perché succede? E come posso prevenire questo comportamento?

Edit: Perché succede è ormai chiaro da varie risposte. Per quanto riguarda come evitare ciò, sembra interessante quanto segue:

class myostream : public ostream { 
    public: 
     // ... 
     myostream &operator<<(char const *str) { 
      std::operator<<(*this, str); 
      return *this; 
     } 
}; 

Tuttavia, questo si traduce in tutti i tipi di ambiguità.

+0

Si potrebbe considerare questa risposta ad un'altra domanda come un punto di partenza per qualcosa è almeno simile a ciò che si desidera ottenere: http://stackoverflow.com/questions/469696/questo-è-suo-molto-utile-cc-snippet/470999#470999 Dovrete aggiungere funzionalità alla classe accettare i modificatori di input (std :: hex, std :: endl ...), ma non dovrebbe essere troppo difficile. –

risposta

6

rvalues ​​non possono essere tenuti a riferimento non-const. Quindi nel tuo esempio il temporaneo di tipo ostream non può essere il primo argomento dell'operatore libero < < (std :: ostream &, char const *) e ciò che viene utilizzato è l'operatore membro < < (void *).

Se ne avete bisogno, è possibile aggiungere una chiamata come

myostream(cout).flush() << "foo"; 

che trasformerà il rvalue in un punto di riferimento.

Si noti che in C++ 0X, l'introduzione del riferimento di rvalue consentirà di sovraccaricare l'operatore < < prendendo come riferimento i riferimenti rvalue, risolvendo la causa principale del problema.

+0

Intendevi "rvalori non possono essere legati ..."? e "... trasforma il valore in un lvalue ..."? –

+0

Giusto, ho corretto la mia risposta. Grazie. – AProgrammer

3

Ho appena realizzato parte della risposta. Il temporaneo non è un lvalue, quindi non può essere utilizzato come argomento di tipo ostream &.

La domanda "come posso fare questo lavoro" rimane ...

+0

Il più semplice: fornire funzioni membro che fanno il lavoro per voi. –

7

Se un oggetto non ha un nome (cioè si tratta di un temporaneo), non può essere associato a un riferimento non-const. In particolare, non può essere vincolata al primo parametro di:

operator<<(ostream &, char *) 
+0

.. fino a quando arriva C++ 0x e ci autorizza con riferimenti r-value. – dirkgently

-1

Beh, non so le specifiche C++ che causa questo, ma è facile da suss fuori perché succede.

Una vita temporanea in pila, in genere da passare a un'altra funzione o per far eseguire un'unica operazione su di essa. Quindi, se si chiama l'operatore libero su di esso:

operatore < < (myostream (cout))

Si è distrutto al termine di questa operazione e il secondo operatore "< <" per aggiungere l'endl sarebbe fare riferimento a un oggetto non valido. Il valore restituito dall'operatore "" libero sarebbe un riferimento a un oggetto temporaneo destructed. Le specifiche del C++ probabilmente definiscono le regole sugli operatori gratuiti per evitare che questo scenario possa frustrare e confondere i programmatori C++.

Ora, nel caso di un "< < (void *)" operatore membro sul temporaneo, il valore restituito è l'oggetto stesso, che è ancora in pila e non distrutto, quindi il compilatore sa di non distruggere ma per passarlo al prossimo operatore membro, quello che prende il file. L'operatore che concatena sui temporari è una caratteristica utile per il codice C++ succinto, quindi sono sicuro che i progettisti di specifiche C++ lo hanno considerato e implementato il compilatore per supportarlo intenzionalmente.

modificare

Alcuni hanno detto che è a che fare con un riferimento non-const.Questo codice viene compilato:

#include <iostream> 
using namespace std; 
class myostream : public ostream { 
    public: 
     myostream(ostream const &other) : 
      ostream(other.rdbuf()) 
     { } 
      ~myostream() { cout << " destructing "; } 
    }; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    basic_ostream<char>& result = std::operator << (myostream(cout), "This works"); 
    std::operator << (result, "illegal"); 
     return 0; 
} 

e restituisce

This works destructing illegal 
+1

Mi dispiace - la tua comprensione della vita dei temporari è sbagliata. –

+0

Per favore sentitevi liberi di illuminarmi. AFAIK, la descrizione qui è coerente con la mia spiegazione: http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr382. htm. Se si utilizza un metodo statico su un temporaneo, il temporaneo deve essere distrutto quando lo stack di chiamate per quell'operazione viene ripulito. Se viene utilizzato come "this" per un'operazione membro, può continuare a essere attivo dopo che l'operazione è stata completata poiché si trova nella parte inferiore dello stack di chiamate, quindi gli operatori membri concatenati funzioneranno ma i metodi statici no. –

+0

Il link che hai postato è una nota fonte di informazioni C++ (e mostra un comportamento strano, spostandomi su un'altra pagina). Lo standard C++ è il riferimento qui e dice (in modo efficace) che il temporaneo deve rimanere in sospeso fino alla fine dell'espressione completa di cui fa parte, che in questo caso è la catena completa degli operatori <<. La vera ragione per cui il codice non funziona come previsto è data nella mia risposta. –

1

Poiché nessuna delle risposte finora sembra dare una soluzione pulita, mi accontenterò della soluzione sporca:

myostream operator<<(myostream stream, char const *str) { 
    std::operator<<(stream, str); 
    return stream; 
} 

Questo è possibile solo perché myostream ha un costruttore di copia. (Internamente, è supportato da un conteggio std::stringbuf.)

0

Mentre C++ 11 risolve questo problema poiché ci sono riferimenti di rvalue, penso che questo potrebbe essere una soluzione alternativa per il pre-C++ 11.

La soluzione è quella di avere una funzione di membro < < operatore dove possiamo lanciare a un riferimento non const alla classe di base:

class myostream : public ostream { 
    public: 
     // ... 
     template<typename T> 
     ostream &operator<<(const T &t) { 
      //now the first operand is no longer a temporary, 
      //so the non-member operators will overload correctly 
      return static_cast<ostream &>(*this) << t; 
     } 
}; 
Problemi correlati