Perché cout print “2 + 3 = 15” in questo snippet di codice?

Perché l’output del programma seguente è quello che è?

#include  using namespace std; int main(){ cout << "2+3 = " << cout << 2 + 3 << endl; } 

produce

 2+3 = 15 

invece del previsto

 2+3 = 5 

Questa domanda è già passata più cicli di chiusura / riapertura.

Prima di votare per chiudere, considera questa meta discussione su questo problema.

Sia intenzionalmente che per caso, hai << alla fine della prima linea di output, dove probabilmente intendevi ; . Quindi essenzialmente

 cout << "2+3 = "; // this, of course, prints "2+3 = " cout << cout; // this prints "1" cout << 2 + 3; // this prints "5" cout << endl; // this finishes the line 

Quindi la domanda si riduce a questo: perché cout << cout; stampare "1" ?

Questo risulta essere, forse sorprendentemente, sottile. std::cout , tramite la sua class base std::basic_ios , fornisce un certo tipo di operatore di conversione che deve essere usato nel contesto booleano, come in

 while (cout) { PrintSomething(cout); } 

Questo è un esempio piuttosto scarso, in quanto è difficile far fallire l'output, ma std::basic_ios è in realtà una class base per i flussi di input e di output, e per l'input ha molto più senso:

 int value; while (cin >> value) { DoSomethingWith(value); } 

(esce dal ciclo alla fine del stream o quando i caratteri del stream non formano un numero intero valido).

Ora, la definizione esatta di questo operatore di conversione è cambiata tra le versioni C ++ 03 e C ++ 11 dello standard. Nelle versioni precedenti, era operator void*() const; (in genere implementato come return fail() ? NULL : this; ), mentre in newer è explicit operator bool() const; (in genere implementato semplicemente come return !fail(); ). Entrambe le dichiarazioni funzionano bene in un contesto booleano, ma si comportano diversamente quando (errate) vengono utilizzate al di fuori di tale contesto.

In particolare, in base alle regole C ++ 03, cout << cout sarebbe interpretato come cout << cout.operator void*() e stamperà un indirizzo. In C ++ 11 regole, cout << cout non dovrebbe compilare affatto, in quanto l'operatore è dichiarato explicit e quindi non può partecipare a conversioni implicite. Questa era in effetti la motivazione principale del cambiamento, impedendo la compilazione del codice senza senso. Un compilatore conforms agli standard non produrrebbe un programma che stampa "1" .

Apparentemente, alcune implementazioni C ++ consentono di combinare il compilatore e la libreria in modo tale da produrre risultati non conformi (citando @StephanLechner: "Ho trovato un'impostazione in xcode che produce 1 e un'altra impostazione che produce un indirizzo: Lingua dialetto c ++ 98 combinato con "Libreria standard libc ++ (libreria standard LLVM con supporto c ++ 11)" produce 1, mentre c ++ 98 combinato con libstdc (libreria standard gnu c ++) produce un indirizzo; "). È ansible avere un compilatore in stile C ++ 03 che non comprenda operatori di conversione explicit (che sono nuovi in ​​C ++ 11) combinati con una libreria in stile C ++ 11 che definisce la conversione come operator bool() . Con un tale mix, diventa ansible che cout << cout venga interpretato come cout << cout.operator bool() , che a sua volta è semplicemente cout << true e stampa "1" .

Come dice Igor, si ottiene questo con una libreria C ++ 11, dove std::basic_ios ha l’ operator bool invece operator void* , ma in qualche modo non è dichiarato (o trattato come) explicit . Vedi qui per la dichiarazione corretta.

Ad esempio, un compilatore C ++ 11 conforms darà lo stesso risultato con

 #include  using namespace std; int main() { cout << "2+3 = " << static_cast(cout) << 2 + 3 << endl; } 

ma nel tuo caso, il static_cast viene (a torto) permesso come una conversione implicita.


Modifica: poiché questo comportamento non è normale o previsto, potrebbe essere utile conoscere la tua piattaforma, la versione del compilatore, ecc.


Modifica 2: per riferimento, il codice di solito dovrebbe essere scritto come

  cout << "2+3 = " << 2 + 3 << endl; 

o come

  cout << "2+3 = "; cout << 2 + 3 << endl; 

e sta mescolando insieme i due stili che hanno esposto il bug.

Il motivo dell’output inatteso è un errore di battitura. Probabilmente intendevi

 cout << "2+3 = " << 2 + 3 << endl; 

Se ignoriamo le stringhe che hanno l'output atteso, restiamo con:

 cout << cout; 

Dal momento che C ++ 11, questo è mal formato. std::cout non è implicitamente convertibile in qualcosa che std::basic_ostream::operator<< (o un overload non membro) accetterebbe. Pertanto un compilatore conforms agli standard deve almeno avvisarti di farlo. Il mio compilatore si è rifiutato di compilare il tuo programma.

std::cout sarebbe convertibile in bool , e il bool overload dell'operatore di input del stream avrebbe l'output osservato di 1. Tuttavia, il sovraccarico è esplicito, quindi non dovrebbe consentire una conversione implicita. Sembra che l'implementazione del tuo compilatore / libreria standard non sia strettamente conforms allo standard.

In uno standard pre-C ++ 11, questo è ben formato. Allora std::cout aveva un operatore di conversione implicito per void* che ha un sovraccarico dell'operatore di input del stream. L'output per quello sarebbe comunque diverso. stamperebbe l'indirizzo di memoria dell'object std::cout .

Il codice postato non dovrebbe essere compilato per alcun compilatore conforms C ++ 11 (o successivo), ma dovrebbe compilare senza nemmeno un avvertimento sulle implementazioni pre C ++ 11.

La differenza è che C ++ 11 ha reso esplicita la conversione di uno stream in un bool:

C.2.15 Clausola 27: Libreria di input / output [diff.cpp03.input.output] 27.7.2.1.3, 27.7.3.4, 27.5.5.4

Modifica: specificare l’uso di esplicito negli operatori di conversione booleani esistenti
Motivazione: chiarire le intenzioni, evitare soluzioni alternative.
Effetto sulla caratteristica originale: il codice valido C ++ 2003 che si basa su conversioni booleane implicite non riuscirà a compilare con questo standard internazionale. Tali conversioni si verificano nelle seguenti condizioni:

  • passare un valore ad una funzione che accetta un argomento di tipo bool;

operatore ostream << è definito con un parametro bool. Poiché una conversione in bool esisteva (e non era esplicita) è pre-C ++ 11, cout << cout stato tradotto in cout << true che restituisce 1.

E secondo C.2.15, questo non dovrebbe più essere compilato a partire da C ++ 11.

Puoi facilmente eseguire il debug del tuo codice in questo modo. Quando usi cout tuo output viene memorizzato nel buffer in modo da poterlo analizzare in questo modo:

Immaginate che la prima occorrenza di cout rappresenti il ​​buffer e che l’operatore << accoda alla fine del buffer. Il risultato dell'operatore << è il stream di uscita, nel tuo caso cout . Si parte da:

cout << "2+3 = " << cout << 2 + 3 << endl;

Dopo aver applicato le regole sopra indicate ottieni una serie di azioni come questa:

buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);

Come ho detto prima il risultato di buffer.append() è il buffer. All'inizio il tuo buffer è vuoto e hai la seguente dichiarazione da elaborare:

statement: buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);

buffer: vuoto

Innanzitutto hai buffer.append("2+3 = ") che mette la stringa data direttamente nel buffer e diventa buffer . Ora il tuo stato assomiglia a questo:

istruzione: buffer.append(cout).append(2 + 3).append(endl);

buffer: 2 + 3   =  

Dopo di che continui ad analizzare la tua dichiarazione e ti imbatti in cout come argomento da aggiungere alla fine del buffer. Il cout viene considerato come 1 quindi aggiungerai 1 alla fine del tuo buffer. Ora sei in questo stato:

istruzione: buffer.append(2 + 3).append(endl);

buffer: 2 + 3   =   1

La prossima cosa che hai nel buffer è 2 + 3 e poiché l'aggiunta ha una precedenza più alta dell'operatore di output, devi prima aggiungere questi due numeri e poi metterai il risultato nel buffer. Dopo ciò ottieni:

istruzione: buffer.append(endl);

buffer: 2 + 3   =   1 5

Infine aggiungi il valore di endl alla fine del buffer e hai:

dichiarazione:

buffer: 2 + 3   =   1 5 \ n

Dopo questo processo, i caratteri dal buffer vengono stampati dal buffer allo standard output uno per uno. Quindi il risultato del tuo codice è 2+3 = 15 . Se guardi questo, ottieni 1 aggiuntivo da te che hai provato a stampare. Rimuovendo << cout dalla tua dichiarazione otterrai l'output desiderato.