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.

23
demandé sur Peter Mortensen 2016-02-21 15:31:01

5 réponses

cela devrait en fait aller à la surcharge char maintenant; malheureusement aucun des compilateurs en question ne mettent en œuvre DR 1601 .

[conv.prom] / 4 :

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.

19
répondu T.C. 2016-03-15 21:46:53

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é.

14
répondu Vlad from Moscow 2016-02-21 12:43:02

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.

11
répondu Christian Hackl 2017-05-23 12:24:14

parce que les deux appellent deux opérateurs différents surcharges:

  • le premier appelle le non-membre operator<< pour std::ostream et char . Ça imprime le personnage.

  • le deuxième exemple appelle le membre operator<< pour int en raison de promotions entières, comme expliqué par d'autres réponses.

7
répondu bolov 2016-02-21 12:47:01

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.

5
répondu Mats Petersson 2016-02-21 12:45:23