【问题标题】:C++11 Fun with initializer lists, arrays, and enumerations初始化列表、数组和枚举的 C++11 乐趣
【发布时间】:2011-12-22 15:51:12
【问题描述】:

背景

C++11 初始化列表可用于初始化向量和数组,并将参数传递给构造函数。

下面有一段代码,我想使用初始化列表初始化这样一个数组,其中包含从 eCOLORS::FirsteCOLORS::Last 的所有 eCOLORS 枚举。

原始码

由于所有信息在编译时都是已知的,我认为有办法解决这个问题。

enum class eCOLORS 
{
    kBLUE=0, kGREEN, kRED, kPURPLE,
        First=kBLUE, Last=kPURPLE 
};

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

struct Foo
{
    Foo( eCOLORS color ) { }
};

int main(int argc, char** argv)
{
    Foo a[2] = {
        { eCOLORS::kRED   },
        { eCOLORS::kGREEN }
    };  // works, but requires manual maintenance if I add another color

    /* how to feed v with all the enums from First to Last
       without hard-coding?
    Foo v[Size<eCOLORS>()] = {

    };
    */
}

丑陋的伪答案

共识似乎是目前没有办法做到这一点。

我问这个问题的初衷是,我想自动 创建一个 Foo 对象数组,其初始化完全基于 eColors 的枚举。我想要一个无需维护的解决方案 即使在eColors 中添加更多条目后也可以使用。

使用来自this earlier post 的 Enum 类,我可以编写一个函数模板,为我提供所需的功能。即使不使用该 Enum 类,您仍然可以从 eCOLORS::First 循环到 eCOLORS::Last,以及一些丑陋的演员表。

我丑陋的伪答案是 kludgy(没有编译时初始化列表那么好),但至少它是零维护。

注意:如果出现更好的解决方案,我会相应地更新 OP。

template <typename T, typename E>
std::vector< T >
Initialize_With_Enums()
{
  std::vector< T > v;
  for( auto p : Enum<E>() )
    v.push_back( T( p ));
  return v;
}

int main( int argc, char** argv )
{
  auto w = Initialize_With_Enum<Foo,eCOLORS>();
}

【问题讨论】:

  • C++11 初始化列表不能用于初始化数组——虽然语法看起来很相似,但数组实际上使用聚合初始化。

标签: c++ initialization c++11


【解决方案1】:

您可以使用可变参数模板以及我将称之为“索引技巧”的东西来做到这一点。

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());
}

这假设所有枚举器都是连续的,并且需要std::underlying_type,这在 GCC 4.6 中不受支持(将在 4.7 中提供,但您可以使用 emulate it to a certain extent)。

【讨论】:

  • 这就是为什么我们需要更强大的静态反射。 max_enumator&lt;&gt;is_enumator&lt;&gt;next_enumator&lt;&gt;。类型特征有很多作用,但我们可以使用更多。
【解决方案2】:

我认为你不能用初始化列表来做到这一点。这不是他们的本意。我认为您可以通过定义一个迭代器来管理一个体面的解决方法,该迭代器将迭代具有FirstLast 成员的任何枚举。

但首先,您对Size 的定义不太正确...

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

声明它constexpr 意味着它的定义是一个编译时常量。所以你可以在模板参数等中使用它。

我现在没有时间为您创建范围类。枚举值和整数对于枚举类不可互换这一事实有点复杂。但这并不太难。您可以使用这个问题“Is there a range class in C++0x (aka C++11) for use with range based for loops?”作为起点。您基本上使用从 [begin, end) 对初始化的向量初始化程序与该问题中讨论的范围类结合使用。

【讨论】:

    【解决方案3】:

    没有使用初始化列表自动执行此操作的方法,但如果您知道枚举的第一个和最后一个值,只需使用 for 循环插入值,您就可以通过算法来执行此操作。

    【讨论】:

    • 我想知道是否有 MACRO 解决方案,那么?当然,函数模板会更好...
    • 但是,您的算法假定 first 和 last 之间的所有值都有一个枚举。
    • @John:是的,尽管问题的粗体部分暗示了这一点。
    【解决方案4】:

    MACRO 解决方案。

    #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]);
        }
    }
    

    【讨论】:

      【解决方案5】:

      我喜欢你的问题。很久以前,这种事情用X宏来处理http://www.drdobbs.com/the-new-c-x-macros/184401387

      我是一个 c++11 新手,但经过一番折腾后,我找到了某种解决方案(g++ 4.8.4):

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

      我保留了您的 Size(),但添加了一些其他样板,以使初始化更简单、更易于阅读。

      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; }
      

      这里有两个神奇之处:

      • 我们返回一个对初始化数组的引用(它本身就是另一个模板参数)
      • 我们在递归调用期间使用垃圾参数初始化静态数组

      像这样:

      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;
      }
      

      最后,它与:

      • 类型定义
      • 数组的静态声明
      • 对被初始化数组的引用

      像这样:

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

      编辑:

      递归初始化函数中的垃圾参数可以通过以下方式删除:

      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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-31
        • 2017-10-27
        • 2011-05-23
        • 2011-10-21
        • 2021-09-21
        • 1970-01-01
        相关资源
        最近更新 更多