10 votes

chaînage des flux, ordre de sortie

J'ai une fonction qui prend un ostream en tant qu'argument, écrit des données dans le flux, puis renvoie une référence à ce même flux, comme suit :

#include <iostream>

std::ostream& print( std::ostream& os ) {
  os << " How are you?" << std::endl;
  return os;
}

int main() {
  std::cout << "Hello, world!" << print( std::cout ) << std::endl;
}

La sortie de ce code est :

 How are you?
Hello, world!0x601288

Cependant, si je sépare les expressions de chaînage en deux déclarations, comme ceci

int main() {
  std::cout << "Hello, world!";
  std::cout << print( std::cout ) << std::endl;
}

alors j'obtiens au moins l'ordre correct dans la sortie, mais j'obtiens toujours une valeur hexagonale :

Hello, world! How are you?
0x600ec8

J'aimerais comprendre ce qui se passe ici. Est-ce qu'une fonction normale a la priorité sur operator<< et c'est pourquoi l'ordre de sortie est inversé ? Quelle est la bonne façon d'écrire une fonction qui insère des données dans un fichier de données ? ostream mais cela peut aussi s'enchaîner avec operator<< ?

5voto

Nawaz Points 148870

Le comportement de votre code est non spécifié conformément à la norme C++.

Explication

Les éléments suivants (j'ai supprimé std::endl pour simplifier)

std::cout << "Hello, world!" << print( std::cout ); 

est équivalent à ceci :

operator<<(operator<<(std::cout, "Hello, World!"), print(std::cout));

qui est un appel de fonction, passant deux arguments :

  • Le premier argument est : operator<<(std::cout, "Hello, World!")
  • Le deuxième argument est : print(std::cout)

Or, la norme ne précise pas l'ordre dans lequel les arguments sont évalués. Elle est non spécifié . Mais votre compilateur semble évaluer le second argument en premier, c'est pourquoi il imprime "Comment allez-vous ?" d'abord, évaluer le second argument à une valeur de type std::ostream& qui est ensuite transmis à l'appel montré ci-dessus (cette valeur est l'objet std::cout lui-même).

Pourquoi une sortie hexadécimale ?

Vous obtenez une sortie hexadécimale parce que le deuxième argument est évalué à std::cout qui est imprimé comme un nombre hexadécimal, parce que std::cout est implicitement converti en valeur de pointeur de void* c'est pourquoi il est imprimé sous forme de nombre hexadécimal.

Essayez ça :

void const *pointer = std::cout; //implicitly converts into pointer type!
std::cout << std::cout << std::endl;
std::cout << pointer << std::endl;

Il imprimera la même valeur pour les deux. Par exemple, cet exemple à idéone imprime ça :

0x804a044
0x804a044 

Notez également que je n'ai pas utilisé explicite plutôt std::cout es implicitement converti en type de pointeur.

J'espère que cela vous aidera.


Quelle est la bonne manière d'écrire une fonction qui insère des données dans un fichier ostream mais cela peut aussi s'enchaîner avec operator<< ?

Quand cela dépend de ce que vous entendez par enchaînement ? Évidemment, ce qui suit ne fonctionnerait pas (comme expliqué ci-dessus) :

std::cout << X << print(std::cout) << Y << Z; //unspecified behaviour!

Peu importe comment vous écrivez print() .

Cependant, celle-ci est bien définie :

print(std::cout) << X << Y << Z; //well-defined behaviour!

3voto

Joachim Isaksson Points 85969

La raison en est que votre fonction print() sera évaluée avant le reste de l'instruction et renverra une référence à cout qui sera ensuite réellement imprimée comme un pointeur (cout << cout). Cet ordre d'évaluation est en fait un comportement non spécifié, mais semble être le cas avec votre compilateur.

Quant à la définition d'une "fonction" consciente du flux qui a en fait un comportement défini avec la même fonctionnalité, cela pourrait fonctionner ;

#include <iostream>

template <class charT, class traits>
  std::basic_ostream<charT,traits>& print ( std::basic_ostream<charT,traits>& os )
{
        os << " How are you?" << std::endl;
        return os;
}

int main() {
  std::cout << "Hello, world!" << print << std::endl;
}

Voir aussi cette réponse pour un peu plus de détails sur ce que "non spécifié" signifie réellement dans ce cas.

2voto

xskxzr Points 5160

Sortie hexadécimale

Avant C++11, la classe std::ostream a une fonction de conversion en void* . Puisque votre print les fonctions de retour std::ostream& lors de l'évaluation de std::cout << print(...) le retour std::ostream lvalue sera implicitement convertie en void* et ensuite être sorti comme une valeur de pointeur. C'est pourquoi il existe une sortie hexadécimale.

Depuis C++11, cette fonction de conversion est remplacé par un explicite la fonction de conversion en bool Donc, si l'on essaie de produire un std::ostream l'objet devient mal formé.

Ordre d'évaluation

Avant C++17, l'opérateur surchargé est considéré comme un appel de fonction pour analyser l'ordre d'évaluation, et l'ordre d'évaluation des différents arguments d'un appel de fonction n'est pas spécifié. Il n'est donc pas étrange que l'opérateur print est évaluée en premier lieu, ce qui entraîne How are you? est sortie en premier lieu.

Depuis C++17, l'ordre d'évaluation des opérandes de l'opérateur << est strictement de gauche à droite, et les opérandes des opérateurs surchargés partagent le même ordre d'évaluation que ceux de l'opérateur surchargé (voir plus de détails). aquí ). Ainsi, votre programme obtiendra toujours la sortie (en supposant que print retourne quelque chose qui peut être édité)

Hello, world! How are you?
something returned by print

EXEMPLE EN DIRECT

1voto

bames53 Points 38303

Dans votre déclaration std::cout << "Hello, world!" << print( std::cout ) << std::endl il n'est pas défini si std::cout << "Hello, world!" se produit avant ou après print( std::cout ) . C'est pourquoi la commande peut ne pas être celle que vous attendez.

La valeur hexagonale vient du fait que vous faites aussi std::cout << std::cout ( print renvoie à std::cout qui est alimenté dans le << chaîne). La main droite std::cout est converti en un void * et c'est imprimé sur la sortie.

1voto

Ben Voigt Points 151460

Cela fonctionnerait, pour combiner print con << et contrôler l'ordre :

print( std::cout << "Hello, world!" ) << std::endl;

Ou, si vous voulez une fonction qui est appelée avec << voir la réponse de Joachim.

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