C++11 初始值设定项列表、数组和枚举的乐趣
C++11 Fun with initializer lists, arrays, and enumerations
>Background
C++11 初始值设定项列表可用于初始化向量和数组,并将参数传递给构造函数。
我在下面有一段代码,我想使用初始值设定项列表初始化这样一个数组,其中包含从 eCOLORS::First
到 eCOLORS::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
中也会起作用。
使用之前文章中的 Enum 类,我可以编写一个函数模板,为我提供所需的功能。 即使不使用Enum类,你仍然可以从eCOLORS::First
循环到eCOLORS::Last
,以及一些丑陋的强制转换。
我丑陋的伪答案很笨拙(没有编译时初始值设定项列表那么好(,但至少它是零维护。
注意:如果出现更好的解决方案,我将相应地更新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>();
}
您可以使用可变参数模板和我称之为"索引技巧"来做到这一点。
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());
}
这假设所有枚举器都是顺序的,并且需要 GCC 4.6 中不支持的std::underlying_type
(将在 4.7 中,但您可以在一定程度上模拟它(。
我认为你不能用初始值设定项列表做到这一点。这不是他们想要的那种东西。我认为您可以通过定义一个迭代器来管理一个不错的解决方法,该迭代器将迭代具有First
和Last
成员的任何枚举。
但首先,你对Size
的定义不太正确......
template< typename E >
constexpr size_t Size()
{
return (size_t)(E::Last) - (size_t)(E::First) + 1;
}
将其声明为constexpr
意味着它的定义是一个编译时常量。因此,您可以在模板参数等中使用它。
我现在没有时间为您创建范围类。由于枚举值和整数对于枚举类不可互换,因此这有点复杂。但这并不难。您可以使用此问题"C++0x(又名 C++11(中是否有范围类用于基于范围的 for 循环?"作为起点。您基本上使用向量初始值设定项,该初始值设定项从 [begin, end( 对与范围类结合,如该问题中所述。
一种使用初始值设定项列表自动执行此操作的方法,但是如果您知道枚举的第一个和最后一个值,只需使用 for
循环插入值,您就可以通过算法执行此操作。
宏解决方案。
#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("%sn", colors[i]);
}
}
你的问题。很久以前,这种事情曾经用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;
}
- 带有枚举方向/类型的气泡排序结构数组
- C++ 指向枚举数组的指针
- 生成整数长度的枚举数组
- C++如何将字符串数组中的一个点设置为枚举变量
- 如何从枚举类值中指定模板函数参数中的数组大小?
- 初始化枚举类 (C++11) 类型的二维 std::数组
- 如何递归枚举每个元素都有上限的所有可能的数组
- 从数组中删除枚举并计算总和
- 如何有效地用枚举填充 2D std::数组
- 数组枚举和输入
- 随机生成一个没有重复元素的枚举数组 (C++)
- 地址数组,带有枚举的返回值
- 错误:数组大小表达式必须具有整型或无作用域枚举类型,而不是'double'
- C++ 中的枚举中的数组
- 枚举与数组
- 枚举数组相对于常量和宏的优势
- 传递枚举数组是什么意思
- 枚举数组错误之前的预期主表达式
- 如何使用枚举初始化向量/数组
- 使用固定值枚举数的枚举零初始化