Mauvaise affectation des valeurs dans le calcul
je jouais avec enums et j'ai essayé de reproduire quelques exemples à partir de cette page . Les premiers exemples ont fonctionné comme prévu, mais j'ai obtenu quelques résultats intéressants avec le code suivant:
#include <iostream>
enum num : char {
zero = '0',
one = '1',
two = '2',
three = '3',
four = '4',
five = '5',
six = '6'
};
int main()
{
const char two = '2';
std::cout << two << std::endl;
std::cout << num::two;
return 0;
}
la sortie est:
2
50
Je m'attendais à ce que les deux résultats soient les mêmes, mais le num::two
semble imprimer une autre valeur. Également cette valeur ne change pas (50)
, donc je suppose que ce n'est pas une valeur aléatoire/poubelle & il y a une sorte de char/analyse interne qui est faite que je ne comprends pas? Ici est la ideone lien .
je sais que je peux le faire fonctionner en assignant comme ceci zero = 0
, sans guillemets simples et il fonctionne. Cependant, je veux savoir ce qui se passe dans les coulisses et comment je pourrais contrôler ce que la valeur non-à un chiffre je peux imprimer via des guillemets affectation.
5 réponses
cela devrait en fait aller à la surcharge char
maintenant; malheureusement aucun des compilateurs en question ne mettent en œuvre DR 1601 .
Un prvalue d'un non délimité type d'énumération dont le sous-jacent est de type fixe ([dcl.enum]) peut être convertie en valeur de sa valeur sous-jacente type.
cela signifie num
peut être promu char
.
en outre, si la promotion intégrale peut être appliquée à son sous-jacent type, une valeur d'un type d'énumération unscoped dont le type sous-jacent est fixe peut également être converti en prvalue du sous-jacent promu type.
donc num
peut aussi être promu int
.
les candidats concernés sont:
template <class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
char ch );
template<class charT, class Traits>
basic_ostream<charT, Traits>& basic_ostream<charT, Traits>::operator<<(int);
pour les deux candidats, le premier argument est une conversion d'identité et le second est une promotion. Les deux numéros num
à char
et num
à int
ont le rang de promotion.
Pre-DR1601, ceux-ci sont tout aussi bons, de sorte que le modèle/Non-modèle tie breaker entre en jeu. Le premier est un modèle de fonction; le second est une fonction de membre simple, donc le second gagne.
DR1601 a ajouté une règle qui dit:
une conversion qui favorise une énumération dont le type sous-jacent est fixe à son type sous-jacent est mieux que favorise l' promus type sous-jacent, si les deux sont différents.
cela signifie que num
à char
est maintenant mieux que num
à int
, de sorte que la première surcharge est maintenant une meilleure correspondance et devrait être sélectionnée.
selon la norme C++ (4.5 promotions intégrales)
4 valeur d'un type d'énumération non dopé dont le type sous-jacent est fixe (7.2) peut être converti en prvalue de son type sous-jacent. en outre, si la promotion intégrale peut être appliquée à son type sous-jacent, une valeur PR d'un type d'énumération non corrigé dont le type sous-jacent est fixe peut aussi être convertie en une valeur pr du type sous-jacent promu tapez .
ainsi la promotion intégrale est appliquée et l'opérateur << pour les objets de type int est appelé.
quand vous dites enum num : char
, alors vous exprimez le fait que num
est implémenté en interne en termes de char
mais peut encore être automatiquement converti en une valeur entière, qui est pas nécessairement char
.
comme la page que vous citez dit:
les valeurs du type d'énumération non dopé sont implicitement convertibles en les types intégraux.
Voir Pourquoi est-il une valeur de l'enum avec un fixe sous-jacent type de char résoudre fct(int) au lieu de fct(char)? pour une discussion intéressante sur les problèmes dans le standard C++ du libellé relatif à la combinaison de la promotion intégrale et fixe sous-jacents types.
dans tous les cas, vous pouvez imaginer tout cela comme une classe avec une variable membre privée char
et un opérateur de conversion public int
:
// very similar to your enum:
class num {
private:
char c;
public:
num(char c) : c(c) {}
operator int() const {
return c;
}
};
num two { '2' };
std::cout << two; // prints 50
pour augmenter la sécurité du type et faire de la ligne std::cout
une erreur de compilation, il suffit de transformer le enum
en un enum class
:
enum class num : char
c'est de nouveau similaire à l'imaginé class num
ci-dessus, mais sans l'opérateur de conversion.
quand vous alimentez une instance de num
à std::cout
, alors vous êtes un client de num
et ne sont pas logiquement censés penser que le format de sortie sera tenir compte de sa mise en œuvre interne char
.
pour obtenir plus de contrôle sur le format de sortie, vous devriez plutôt faire comme avec tout autre type personnalisé, et surcharge operator<<
pour std::ostream
. Exemple:
#include <iostream>
enum class num : char {
zero = '0',
one = '1',
two = '2',
three = '3',
four = '4',
five = '5',
six = '6'
};
std::ostream& operator<<(std::ostream& os, num const& n)
{
switch (n)
{
case num::zero: os << "Zero"; break;
case num::one: os << "One"; break;
case num::two: os << "Two"; break;
case num::three: os << "Three"; break;
// and so on
}
return os;
}
int main()
{
std::cout << num::two; // prints "Two"
}
bien sûr, les valeurs spécifiques char
des instances d'enum sont maintenant devenues assez inutiles, donc vous pouvez aussi bien vous en débarrasser complètement:
enum class num : char {
zero,
one,
two,
three,
four,
five,
six
};
cela peut frapper vous comme étrange, mais gardez à l'esprit qu'un enum qui ne représente rien d'autre que des nombres génériques de zéro à six n'est pas un cas d'utilisation réaliste.
parce que les deux appellent deux opérateurs différents surcharges:
-
le premier appelle le non-membre
operator<<
pourstd::ostream
etchar
. Ça imprime le personnage. -
le deuxième exemple appelle le membre
operator<<
pourint
en raison de promotions entières, comme expliqué par d'autres réponses.
la raison est que votre enum : char
n'est pas le même que char
(ce qui est exactement ce que nous voulons - nous ne voulons pas qu'enum soit le même que les autres types, même s'ils sont compatibles avec les assignations - nous voulons que void func(num n)
soit distinct de void func(char n)
, n'est-ce pas?).
donc , puisque enum num
n'est pas un char
, le operator<<(int)
sera utilisé, et imprime la valeur entière, même si le type sous-jacent est char
. Pas raisonnable, mais je suis sûr que c' est ce qui se passe.