Using enum in loops and value consistency

前端 未结 2 1036
傲寒
傲寒 2020-12-10 20:16

I\'m a big fan of C++\'s strong-typing features and what I like the most is to use enumerations while dealing with limited sets of data.

But enumerations lack some u

相关标签:
2条回答
  • 2020-12-10 20:49

    As you've noticed, enum in C++ is not an enumerated type, but something more complex (or more mixed). When you define an enum, you define in fact two things:

    1. An integral type with a legal range sufficient to contain an or of all of the enumerated values. (Technically: the range is 2^n - 1, where n is the number of bits necessary to hold the largest value.)

    2. A series of named constants having the newly defined type.

    (I'm not sure what happens with regards to the range if you explicitly specify an underlying type.)

    Given your enum Prime, for example, the legal values would be all integers in the range [0...64), even if all of these values don't have a name. (At least if you didn't specifically say that it should be an int.)

    It's possible to implement an iterator for enums without initializers; I have a program which generates the necessary code. But it works by maintaining the value in an integral type which is large enough to contain the maximum value plus one. My machine generated implementations of ++ on such an enum will assert if you try to increment beyond the end. (Note that your first example would require iterating h one beyond the last value: my implementation of the various operators does not allow this, which is why I use an iterator.)

    As to why C++ supports the extended range: enum are often used to define bit masks:

    enum X
    {
        a = 0x01,
        b = 0x02,
        c = 0x04,
        all = a | b | c,
        none = 0
    };
    
    X operator|( X lhs, X rhs )
    {
        return X((int)lhs | (int)rhs);
    }
    //  similarly, &, |= and &=, and maybe ~
    

    One could argue that this use would be better handled by a class, but the use of enum for it is ubiquitous.

    (FWIW: my code generator will not generate the ++, -- and the iterators if any of the enum values has an explicitly defined value, and will not generate |, & etc. unless all of the values have explicitly defined values.)

    As to why there is no error when you convert some value outside the legal range (e.g. 100, for X, above) is simply in keeping with the general philosophy inherited from C: it's better to be fast than to be correct. Doing extra range checking would entail additional runtime cost.

    Finally with regards to your last example: I don't see this as a realistic use of enum. The correct solution here is an int[]. While the C++ enum is rather a mixed breed, I would only use it as a real enumerated type, or for bit masks (and only for bit masks because it is such a widely established idiom).

    0 讨论(0)
  • 2020-12-10 20:51

    You can use a switch:

    class Invalid {};
    Prime& operator ++(Prime& p)
    {
        switch(p)
        {
            case n00: return n01;
            case n01: return n02;
            case n02: return n03;
            case n03: return n04;
            case n04: return n05;
            case n05: return n06;
            case n06: return n07;
            case n07: return n08;
            case n08: return n09;
            case n09: return n10;
            case n10: return n11;
            case n11: return n12;
            case n12: return n13;
            case n13: return n14;
            case n14: return n15;
            // Here: 2 choices: loop or throw (which is the only way to signal an error here)
            case n15: default: throw Invalid();
        }
    }
    

    But note that this is not the right use of enums. I personally find this error-prone. If you want to enumerate integers, you can use an array of ints to do this, or for the case of prime numbers, a function (in mathematical sense: int nextPrime(int)).

    0 讨论(0)
提交回复
热议问题