125 votes

Pourquoi cout affiche-t-il "2 + 3 = 15" dans cet extrait de code?

Pourquoi le résultat du programme ci-dessous est-il ce qu'il est?

 #include <iostream>
using namespace std;

int main(){

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

produit

 2+3 = 15
 

au lieu de l'attendu

 2+3 = 5
 

Cette question est déjà passée par plusieurs cycles de fermeture / réouverture.

Avant de voter pour la clôture, veuillez considérer cette méta discussion sur ce sujet.

227voto

Igor Tandetnik Points 13562

Que ce soit intentionnellement ou par accident, vous disposez d' << à la fin de la première sortie de ligne où vous avez probablement censé ;. Donc, vous avez

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

Donc, la question se résume à ceci: pourquoi est - cout << cout; imprimer "1"?

Cela s'avère être peut-être étonnamment, subtile. std::cout, par l'intermédiaire de sa classe de base std::basic_ios, fournit un certain type d'opérateur de conversion qui est destiné à être utilisé dans le contexte booléen, comme dans

while (cout) { PrintSomething(cout); }

C'est un assez mauvais exemple, car il est difficile d'obtenir une sortie à l'échec - mais std::basic_ios est en fait une classe de base pour les entrées et les flux de sortie et d'entrée, il fait beaucoup plus de sens:

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

(sort de la boucle à la fin du flux, ou lorsque les flux de caractères ne forment pas un entier valide).

Maintenant, la définition exacte de cette opérateur de conversion a changé entre le C++03 et C++11 versions de la norme. Dans les anciennes versions, il a été operator void*() const; (généralement mis en œuvre en tant que return fail() ? NULL : this;), tandis que dans les plus récents c'est explicit operator bool() const; (généralement mis en œuvre simplement en tant que return !fail();). Les deux déclarations du bon travail, dans un contexte booléen, mais se comportent différemment lorsqu' (sig)utilisé en dehors d'un tel contexte.

En particulier, sous C++03 règles, cout << cout serait interprété comme cout << cout.operator void*() et imprimer des adresses. En vertu de C++11, les règles, cout << cout ne devrait pas compiler à tous, que l'opérateur est déclaré explicit et ne peut donc pas participer à des conversions implicites. C'est en fait la principale motivation pour le changement de la prévention absurde de code à partir de la compilation. Un compilateur qui est conforme soit à la norme ne serait pas produire un programme qui imprime "1".

Apparemment, certains C++ implémentations permettent de mélanger et assortir le compilateur et de la bibliothèque, de telle manière que le produit non conforme résultats (en citant @StephanLechner: "j'ai trouvé un réglage dans xcode qui produit 1, et un autre paramètre qui donne une adresse: dialecte du Langage c++98 combinée avec la "bibliothèque Standard libc++ (LLVM de la bibliothèque standard du c++11)" les rendements de 1, alors que c++98 combiné avec libstdc (gnu c++ standard library) donne une adresse;"). Vous pouvez avoir un C++03-style compilateur de ne pas comprendre explicit opérateurs de conversion (qui sont nouveaux dans C++11), combiné avec le C++11 bibliothèque de style qui définit la conversion, comme operator bool(). Avec un tel mélange, il devient possible pour cout << cout être interprétés en cout << cout.operator bool(), qui à son tour est tout simplement cout << true et imprime "1".

45voto

Useless Points 18909

Igor dit, vous obtenez ceci avec un C++11 de la bibliothèque, où std::basic_ios a operator bool , au lieu de l' operator void*, mais de toute façon n'est pas déclaré (ou traités) explicit. Voir ici pour la déclaration correcte.

Par exemple, conforme C++11 compilateur va donner le même résultat avec

#include <iostream>
using namespace std;

int main() {
    cout << "2+3 = " << 
    static_cast<bool>(cout) << 2 + 3 << endl;
}

mais dans votre cas, l' static_cast<bool> est (à tort) admis comme une conversion implicite.


Edit: Car ce n'est pas habituel ou à un comportement attendu, il peut être utile de connaître votre plate-forme, version de compilateur, etc.


Edit 2: Pour la référence, le code devrait généralement être écrite comme

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

ou comme

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

et c'est le mélange des deux styles de ensemble, qui a exposé le bug.

21voto

user2079303 Points 4916

La raison de l'inattendu de sortie est une faute de frappe. Vous avez probablement censé

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

Si nous ignorons les chaînes qui ont de la sortie attendue, on se retrouve avec:

cout << cout;

Depuis C++11, c'est mal formé. std::cout n'est pas implicitement convertible à tout ce qui std::basic_ostream<char>::operator<< (ou un non-membre de surcharge) accepteraient. Par conséquent, une des normes conformes compilateur doit au moins vous avertir pour ce faire. Mon compilateur refuse de compiler votre programme.

std::cout seraient convertibles bool, et le bool surcharge du flux d'entrée de l'opérateur aurait observé de sortie de 1. Cependant, la surcharge est explicite, ce qui ne devrait pas permettre une conversion implicite. Il semble que votre compilateur/bibliothèque standard de mise en œuvre n'est pas strictement conforme à la norme.

Dans un pré-C++11, c'est bien formée. Retour ensuite, std::cout avaient une conversion implicite de l'opérateur de void* qui a un flux d'entrée de l'opérateur de surcharge. La sortie de ce serait toutefois différente. il serait d'imprimer l'adresse de la mémoire de l' std::cout objet.

11voto

Serge Ballesta Points 12850

Posté le code ne devrait pas compiler du C++11 (ou version ultérieure conforme compilateur), mais ça devrait compiler sans même un avertissement sur l'avant C++11 implémentations.

La différence est que le C++11 fait de la conversion d'un flux à un bool explicite:

C. 2.15 article 27: Entrée/sortie de la bibliothèque [diff.cpp03.d'entrée.sortie] 27.7.2.1.3, 27.7.3.4, 27.5.5.4

Modification: Spécifiez l'utilisation de l'explicite à l'existant boolean opérateurs de conversion
Justification: Clarifier les intentions, d'éviter les solutions de contournement.
Effet sur la fonction originale: Valide en C++ 2003 code qui repose sur l'implicite boolean conversions ne parviennent pas à compiler avec cette Norme Internationale. De telles conversions se produisent dans les conditions suivantes:

  • le passage d'une valeur à une fonction qui prend un argument de type bool;
    ...

ostream de l'opérateur << est définie avec une valeur bool paramètre. Comme une conversion en booléen existe (et n'était pas explicite) est pré-C++11, cout << cout a été traduit en cout << true ce qui donne 1.

Et d'après C. 2.15, cela ne devrait pas non plus de la compilation à partir de C++11.

7voto

Ivan Kulezic Points 327

Vous pouvez facilement déboguer votre code de cette façon. Lorsque vous utilisez cout votre sortie est mise en mémoire tampon de sorte que vous pouvez l'analyser comme ceci:

Imaginez première occurence de cout représente le tampon et l'opérateur << représente l'ajout à la fin de la mémoire tampon. Résultat de l'opérateur << est flux de sortie, dans votre cas cout. Vous démarrez à partir de:

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

Après l'application des règles énoncées ci-dessus, vous obtenez un ensemble d'actions comme ceci:

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

Comme je l'ai dit avant que le résultat de l' buffer.append() est un tampon. Au début de votre tampon est vide, et vous avez la déclaration suivante à traiter:

déclaration: buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);

tampon: vide

D'abord, vous avez buffer.append("2+3 = ") ce qui place la chaîne donnée directement dans la mémoire tampon et devient buffer. Maintenant, votre état ressemble à ceci:

déclaration: buffer.append(cout).append(2 + 3).append(endl);

tampon: 2+3 =

Après que vous continuez à analyser votre déclaration et vous venez à travers cout comme argument à ajouter à la fin de la mémoire tampon. L' cout est considérée comme 1 de sorte que vous ajoutera 1 à la fin de votre tampon. Maintenant vous êtes dans cet état:

déclaration: buffer.append(2 + 3).append(endl);

tampon: 2+3 = 1

La prochaine chose que vous avez en mémoire tampon est 2 + 3 et depuis plus a une priorité supérieure à celle de sortie de l'opérateur, vous allez d'abord l'ajout de ces deux nombres, et vous permettra de mettre le résultat dans la mémoire tampon. Après que vous obtenez:

déclaration: buffer.append(endl);

tampon: 2+3 = 15

Enfin, vous ajoutez de la valeur de endl à la fin de la mémoire tampon et que vous avez:

déclaration:

tampon: 2+3 = 15\n

Après ce processus, les caractères à partir de la mémoire tampon sont imprimés à partir de la mémoire tampon de sortie standard, un par un. Donc le résultat de votre code est - 2+3 = 15. Si vous regardez ce que vous obtenez supplémentaire 1 de cout vous avez essayé d'imprimer. En supprimant << cout de votre relevé de compte, vous obtiendrez le résultat souhaité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X