2010-04-21 7 views
1

J'utilise l'opérateur de flux < < et l'opérateur de décalage de bits < < en une ligne. Je suis un peu confus, pourquoi le code A) ne produit pas la même sortie que le code B)?opérateur <<: std :: cout << i << (i << 1);

A)

int i = 4; 
std::cout << i << " " << (i << 1) << std::endl; //4 8 

B)

myint m = 4; 
std::cout << m << " " << (m << 1) << std::endl; //8 8 

classe Myint:

class myint { 
    int i; 
public: 
    myint(int ii) { 
     i = ii; 
    } 
    inline myint operator <<(int n){ 
     i = i << n; 
     return *this; 
    } 
    inline operator int(){ 
     return i; 
    } 
}; 

merci à l'avance
Oops

+0

Près d'un double de: http://stackoverflow.com/questions/2603312/the-result-of-int-c0- coutcc /. Pour la plupart des buts pratiques, ils sont identiques, même si cela a utilisé "++" au lieu de "<<" comme opérateur qui a fait la modification –

+2

@Jerry Coffin: ils sont assez similaires mais il y a tout << vs <

Répondre

8

Votre deuxième exemple est un comportement indéfini.

Vous avez défini l'opérateur << sur votre classe myint comme s'il s'agissait en fait de <<=. Lorsque vous exécutez i << 1, la valeur dans i n'est pas modifiée, mais lorsque vous exécutez m << 1, la valeur dans mest modifiée. En C++, c'est un comportement indéfini à la fois lire et écrire (ou écrire plus d'une fois) à une variable sans un point de séquence intermédiaire, ce que les appels de fonctions et les opérateurs ne sont pas, par rapport à leurs arguments. Il est non déterministes si le code

std::cout << m << " " << (m << 1) << std::endl; 

de sortie de la première volonté m avant ou après m est mis à jour par m << 1. En fait, votre code peut faire quelque chose de totalement bizarre, ou s'écraser. Un comportement indéfini peut conduire à littéralement n'importe quoi, alors évitez-le.

Une des bonnes façons de définir l'opérateur << pour myint est:

myint operator<< (int n) const 
{ 
    return myint(this->i << n); 
} 

(le this-> n'est pas strictement nécessaire, juste mon style quand je surcharger les opérateurs)

+1

Mais les opérateurs surchargés sont des appels de fonction Il y a * beaucoup * de points de séquence. –

+2

'std :: cout << m <<" "<< (m << 1)' est équivalent à 'std :: cout.operator << (m) .operator << (" ") .operator << (m.operator << (1)) 'Il existe un point de séquence entre l'invocation de chacun des opérateurs les plus externes, mais il n'y a pas de point de séquence entre les évaluations des arguments transmis aux opérateurs, y compris l'appel interne à 'operator <<'. En d'autres termes, dans 'f (a()) .g (b())', il y a un point de séquence entre l'appel à 'f' et l'appel' g' , mais l'ordre des appels à 'a' et' b' n'est pas défini. –

+0

Peut-être que vous devriez mettre à jour votre réponse parce que sans l'explication dans votre commentaire, il est loin d'être évident qu'aucun des nombreux points de séquence ne garantit qu'il n'y en a pas entre les expressions cruciales. –

2

Votre < < opérateur est en fait < < = opérateur. Si vous remplacez la ligne avec

std::cout << i << " " << (i <<= 1) << std::endl; //8 8 

vous devriez obtenir 8 8.

+6

Vous avez mis en évidence le problème, mais votre "réponse" a un comportement indéfini –

1

bien (m < < 1) est évaluée avant m et donc m détient 8 déjà, comme dans votre opérateur < < vous écrasez votre propre valeur.

Ceci est un comportement incorrect de votre côté, l'opérateur < < doit être const et ne pas modifier votre objet.

5

Parce que int < < X renvoie un nouvel int. myint < < X modifie le myint actuel. Votre opérateur myint < < devrait être fixé pour faire le premier.

La raison pour laquelle vous obtenez 8 pour la première est que m < < 1 est appelée en premier dans votre implémentation. L'implémentation est libre de les faire dans n'importe quel ordre.

0

Parce que l'opérateur << de myint modifie ses lhs. Donc, après avoir évalué m << 1, m aura réellement la valeur 8 (alors que i << 1 ne renvoie que 8, mais ne rend pas i égal à 8). Comme il n'est pas spécifié si m<<1 s'exécute avant cout << m (car il n'est pas spécifié dans quel ordre les arguments d'une fonction ou d'un opérateur sont évalués), il n'est pas spécifié si la sortie sera 8 8 ou 4 8.

2

depuis m est un myInt votre deuxième exemple pourrait être réécrite comme:

std::cout << m << " " << (m.operator<<(1)) << std::endl; 

L'ordre d'évaluation pour les sous-expressions m et (m.operator<<(1)) est non spécifiée, donc il n'y a pas dit que « m » vous obtiendrez pour la 1ère expression utilisée dans m (expression simple m). Donc, vous pourriez obtenir un résultat de "4 8" ou vous pourriez obtenir "8 8".

Notez que l'instruction ne se traduit pas par un comportement non défini, car il y a des points séquence (au moins un appel de fonction) entre le moment où m est modifié et quand il est « lu ». Mais l'ordre d'évaluation des sous-expressions n'est pas spécifié, alors que le compilateur doit produire un résultat (il ne peut pas planter - du moins pas légitimement), on ne sait pas lequel des deux résultats possibles il devrait produire. Donc l'instruction est à peu près aussi utile que celle qui a un comportement indéfini, ce qui veut dire que ce n'est pas très utile.

0

Le langage C++ ne définit pas l'ordre d'évaluation des opérateurs. Il ne définit que leur associativité.

Étant donné que vos résultats dépendent de l'évaluation de la fonction operator<< dans l'expression, les résultats ne sont pas définis.

Algebraic operator $ fonctions doivent toujours être const et retourner un nouvel objet:

inline myint operator <<(int n) const { // ensure that "this" doesn't change 
    return i << n; // implicit conversion: call myint::myint(int) 
}