C++11 Fun with initializer lists, arrays, and enumerations

后端 未结 5 1746
天涯浪人
天涯浪人 2021-01-03 03:27

Background

C++11 initializer lists can be used to initialize vectors and arrays with argument passing to constructors.

I have a piece of code below

相关标签:
5条回答
  • 2021-01-03 03:39

    I like your problem. Long ago, this kind of thing used to be handled with X macros http://www.drdobbs.com/the-new-c-x-macros/184401387

    I'm a c++11 newb, but after some fiddling around I've got some kind of solution (g++ 4.8.4):

    enum class Symbols { FOO, BAR, BAZ, First=FOO, Last=BAZ };
    

    I kept your Size() but added some other boilerplate to make the initialization, lower down, easier to read.

    template< typename E > constexpr size_t Size() { return (size_t)(E::Last) - (size_t)(E::First) + 1; }
    template< typename E > constexpr size_t as_sizet( E s ) { return (size_t)s; }
    template< typename E > constexpr E operator++( E& s, int ) { return (E)(1 + (size_t)s); }
    template< typename E > constexpr bool operator<=( E& a, E& b ) { return (size_t)a < (size_t)b; }
    

    There are two bits of magic here:

    • We return a reference to the initialized array (itself another template parameter)
    • We initialize a static array during the recursive call using a junk argument

    Like so:

    template< typename E, typename EARR > 
    constexpr EARR& init_array( EARR& zArr, E sym = E::First, E junk = E::Last )
    {
        return sym <= E::Last ? init_array( zArr, sym++, zArr[ as_sizet( sym ) ] = sym ) : zArr;
    }
    

    In the end, it comes together with:

    • The typedef
    • The static declaration of the array
    • The reference to the array that gets initialized

    Like so:

    typedef Symbols SymbolArr[ Size<Symbols>() ];
    static SymbolArr symbolArr;
    SymbolArr& symbolArrRef = init_array<Symbols, SymbolArr>(symbolArr);
    

    Edit:

    The junk parameter in the recursive initialization function can be removed using:

    template< typename E > constexpr E next( E& s ) { return (E)(1 + (size_t)s); }
    
    template< typename E, typename EARR > 
    constexpr EARR& init_array( EARR& zArr, E sym = E::First )
    {
        return sym <= E::Last ? init_array( zArr, next( zArr[ as_sizet( sym ) ] = sym ) ) : zArr;
    }
    
    0 讨论(0)
  • 2021-01-03 03:57

    I don't think you can do this with initializer lists. This isn't the sort of thing they're meant for. I think you could manage a decent workaround by defining an iterator that would iterate over any enumeration that had a First and Last member.

    But first, your definition of Size isn't quite right...

    template< typename E >
    constexpr size_t Size()
    {
        return (size_t)(E::Last) - (size_t)(E::First) + 1;
    }
    

    Declaring it constexpr means that it's definition is a compile time constant. So you can use it in template arguments and the like.

    I don't have time right now to create the range class for you. It's somewhat complicated by the fact that enum values and integers are not interchangeable for enum classes. But it's not too hard. You might use this question "Is there a range class in C++0x (aka C++11) for use with range based for loops?" as a starting point. You basically use the vector initializer that initializes from a [begin, end) pair in conjuction with a range class like is discussed in that question.

    0 讨论(0)
  • 2021-01-03 03:58

    You can do this with variadic templates and what I'm going to call the "indices trick".

    typedef std::underlying_type<eCOLORS>::type underlying;
    
    // just a type to carry around variadic pack of numbers
    template <underlying...> struct indices {};
    
    // A template to build up a pack of Count numbers from first
    // third parameter is an accumulator
    template <underlying First, underlying Count, typename Acc = indices<>>
    struct make_indices;
    
    // base case
    template <underlying X, underlying... Acc>
    struct make_indices<X, 0, indices<Acc...>> { typedef indices<Acc...> type; };
    // recursive build up of the pack
    template <underlying First, underlying Count, underlying... Acc>
    struct make_indices<First, Count, indices<Acc...>>
        : make_indices<First, Count-1, indices<First+Count-1, Acc...>> {};
    
    size_t const max_colors = underlying(eCOLORS::Last) - underlying(eCOLORS::First)+1;
    
    // shortcut
    typedef make_indices<
              underlying(eCOLORS::First),
              max_colors
            >::type all_eCOLORS_indices;
    
    // takes a dummy parameter with the pack we built
    template <underlying... Indices>
    std::array<eCOLORS, max_colors> const& all_colors(indices<Indices...>) {
        // convert each number to the enum and stick it in an static array
        static std::array<eCOLORS, max_colors> const all = {
            eCOLORS(Indices)...
        };
        return all;
    }
    
    std::array<eCOLORS, max_colors> const& all_colors() {
        // create a dummy object of the indices pack type and pass it
        return all_colors(all_eCOLORS_indices());
    }
    

    This assumes all the enumerators are sequential, and needs std::underlying_type which is not supported in GCC 4.6 (will be in 4.7, but you can emulate it to a certain extent).

    0 讨论(0)
  • 2021-01-03 03:59

    The MACRO solution.

    #include <stdio.h>
    #include <initializer_list>
    
    #define COLORS(V,E) \
        V(RED) \
        V(GREEN) \
        E(BLUE)
    
    #define COMMA(V) \
        V,
    
    #define NCOMMA(V) \
        V
    
    #define SCOMMA(V) \
        #V,
    
    #define SNCOMMA(E) \
        #E
    
    enum Colors {
        COLORS(COMMA,NCOMMA)
    };
    
    const char * colors[] = {
        COLORS(SCOMMA,SNCOMMA)
    };
    
    #define INIT_LIST(V) \
        { V(COMMA,NCOMMA) }
    
    int main(int argc, char  **argv) {
        for ( auto i : INIT_LIST(COLORS) ) {
            printf("%s\n", colors[i]);
        }
    }
    
    0 讨论(0)
  • 2021-01-03 04:01

    There isn't an automatic way to do this using initializer lists, but you could do it algorithmically if you know the first and last values of the enum by just using a for loop to insert the values.

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