C ++ 11提供了一个改进的enum
和enum struct
。
但是这仍然会受到肯定的影响 - 直到你习惯了它
- 劣等老enum
最令人惊讶的陷阱:a的价值
enum struct E
类型的变量不必是任何枚举的变量
可以是E
基础积分类型范围内的任何值:
enum struct X { one = 1};
X x = X(); // Fine! The value of x is int(0)
x = X(2); // Cool! The value of x is int(2)
您使用enum
或enum struct
类型E
的对象
必须抓住“不是E
s”之一的情况。
如何定义可以使用的通用类型
代替enum struct
(不一定是替补)
具有实例化类的对象不能的属性
假设“枚举”之外的值?
我的意思是不能在某种意义上,如果对象会抛出,那么就会感到满意
假设任何不可区分的值(即 it 捕获案例
它将变成“不是E
s之一”。
我在吓唬报价中说“列举”因为它似乎不可避免
(不使用宏)这些值将被“枚举”
通过一系列整数类型模板参数而不能
访问方便X::one
。
如果这是不可避免的,只要是“枚举”值就可以了
通过类型的枚举索引成为可静态检索的常量。
(然后,客户端代码将meangingful符号与其中任何一个相关联将很简单
索引或索引值,并封装这样方便
映射 - 例如在struct
- 嵌套的匿名enum
!)
对于这个问题我是否已经有一个备受好评的解决方案 知道吗?
通过提交者请求(阿里)继续
你能发一些伪代码吗?它应该显示你想如何使用它。
以下是所需用法的一些迹象(我认为):
/* 1 */
/* These 2 structs could just be tag-types X{}, Y{}
serving to distinguish value-safe-enums that
would otherwise be the same. But you could
also get more mileage out them, as shown...
*/
struct turn;
struct hand;
using vs_turn = val_safe_enum<turn,int,1,2>;
using vs_hand = val_safe_enum<hand,int,1,2>;
struct turn
{
// Want an anonymous scoped enum; only rvalue
enum {
right = vs_turn::which<0>(), // = 1
left = vs_turn::which<1>() // = 2
};
};
struct hand
{
enum {
right = vs_hand::which<0>(), //= 1
left = vs_hand::which<1>() // = 2
};
};
/* End 1 */
/* 2 */
char const * foo(vs_turn const & t) {
// Only 2 possibilities!
return int(t) == turn::right ? "right turn" : "left turn";
}
char const * foo(vs_hand const & h) {
return int(h) == hand::right ? "right hand" : "left hand";
}
/* End 2 */
vs_turn t1(0); // 3.a OK
vs_turn t2(turn::right); // 3b. OK
vs_hand h1(hand::left); // 3c. OK
vs_hand h2(1); // 3d. OK
t1 == vs_turn(turn::right); // 4. OK
t1 < t2; // 5. OK
t1 = t2; // 6. OK
int(t1) == turn::right; // 7. OK. Explicit conversion to underlying type.
/* 8 */
switch(int(t1)) {
case turn::right:
/* Something */ break;
case turn::left:
/* Something */;
// No default necessary!
}
/* End 8 */
vs_turn t3(3); // 9. Throw! Value out of range
vs_turn t4; // 10. Error. No default construction.
t1 == turn::right; // 11a. Error. No Conversion
t1 <= h1; // 11b. Error. No conversion.
t1 = turn::right; // 11c. Error. No conversion
t1 = h1; // 11d. Error. No conversion.
foo(turn::right); // 11e. Error. No conversion
答案 0 :(得分:1)
struct C
{
enum E { a, b, c };
C(E e) : e(e) { if (e > c) throw logic_error; }
private:
E e;
};
<强>更新强>
template<typename T, T... E> struct Check;
template<typename T> struct Check<T> { void check(T) { throw logic_error; } }
template<typename T, T e0, T... E> struct Check<T, e0, E...>
{
void check(T e)
{
if (e != e0)
Check<T, E>::check(e);
}
}
template<typename T, T... E>
struct C
{
C(T e) : e(e) { Check<T, E...>::check(e); }
private:
T e;
}
答案 1 :(得分:0)
这个怎么样?
struct MyColor {
public:
static MyColor Red; // defined as MyColor(red)
static MyColor Blue; // defined as MyColor(blue)
// Copy constructors, assignment etc. goes here
private:
MyColor(Value value);
enum Value { red, blue, green };
Value value;
};
答案 2 :(得分:0)
它很丑,但在编译时失败了:
enum E { a, b, c };
constexpr E range_check(E e) {
return e <= c ? e : (throw "enum value out of range"); // fails at compile time, see below
}
class wrapper {
public:
constexpr wrapper(E e) : e(range_check(e)) { }
private:
enum E e;
};
int main() {
constexpr wrapper w((E)42);
}
我不喜欢这种方法,因为错误信息没有帮助。然而,it is guaranteed在编译时失败。
这是你在找什么?
答案 3 :(得分:0)
在过去的一周里,我一直在寻找一种方法来做你想要的,等等。 以下是我提出的建议:
#define STD_ENUM_ENTRY_WITHOUT_VALUE__(ENUM, NAME) NAME,
#define STD_ENUM_ENTRY_WITHOUT_VALUE_(ENUM, NAME) STD_ENUM_ENTRY_WITHOUT_VALUE__(ENUM, NAME)
#define STD_ENUM_ENTRY_WITHOUT_VALUE(ENUM, SPLIT...) STD_ENUM_ENTRY_WITHOUT_VALUE_(ENUM, SPLIT)
#define STD_ENUM_ENTRY_WITH_VALUE__(ENUM, NAME, VALUE) NAME = VALUE,
#define STD_ENUM_ENTRY_WITH_VALUE_(ENUM, NAME, VALUE) STD_ENUM_ENTRY_WITH_VALUE__(ENUM, NAME, VALUE)
#define STD_ENUM_ENTRY_WITH_VALUE(ENUM, SPLIT...) STD_ENUM_ENTRY_WITH_VALUE_(ENUM, SPLIT)
#define FP_PP_ENUM_STD_ENTRY_1(ENUM, SPLIT...) STD_ENUM_ENTRY_WITHOUT_VALUE(ENUM, SPLIT)
#define FP_PP_ENUM_STD_ENTRY_2(ENUM, SPLIT...) STD_ENUM_ENTRY_WITH_VALUE(ENUM, SPLIT)
#define FP_PP_ENUM_STD_ENTRY__(N, ENUM, SPLIT...) FP_PP_ENUM_STD_ENTRY_##N(ENUM, SPLIT)
#define FP_PP_ENUM_STD_ENTRY_(N, ENUM, VALUE) FP_PP_ENUM_STD_ENTRY__(N, ENUM, FP_PP_EXPAND VALUE)
#define FP_PP_ENUM_STD_ENTRY(ENUM, VALUE) FP_PP_ENUM_STD_ENTRY_(FP_PP_NUM_ARGS VALUE, ENUM, VALUE)
#define FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE__(ENUM, NAME) ::fp::enum_entry<ENUM>(ENUM::NAME, #NAME),
#define FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE_(ENUM, NAME) FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE__(ENUM, NAME)
#define FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE(ENUM, SPLIT...) FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE_(ENUM, SPLIT)
#define FP_PP_ENUM_EXT_ENTRY_WITH_VALUE__(ENUM, NAME, VALUE) ::fp::enum_entry<ENUM>(ENUM::NAME, #NAME),
#define FP_PP_ENUM_EXT_ENTRY_WITH_VALUE_(ENUM, NAME, VALUE) FP_PP_ENUM_EXT_ENTRY_WITH_VALUE__(ENUM, NAME, VALUE)
#define FP_PP_ENUM_EXT_ENTRY_WITH_VALUE(ENUM, SPLIT...) FP_PP_ENUM_EXT_ENTRY_WITH_VALUE_(ENUM, SPLIT)
#define FP_PP_ENUM_EXT_ENTRY_1(ENUM, SPLIT...) FP_PP_ENUM_EXT_ENTRY_WITHOUT_VALUE(ENUM, SPLIT)
#define FP_PP_ENUM_EXT_ENTRY_2(ENUM, SPLIT...) FP_PP_ENUM_EXT_ENTRY_WITH_VALUE(ENUM, SPLIT)
#define FP_PP_ENUM_EXT_ENTRY__(N, ENUM, SPLIT...) FP_PP_ENUM_EXT_ENTRY_##N(ENUM, SPLIT)
#define FP_PP_ENUM_EXT_ENTRY_(N, ENUM, VALUE) FP_PP_ENUM_EXT_ENTRY__(N, ENUM, FP_PP_EXPAND VALUE)
#define FP_PP_ENUM_EXT_ENTRY(ENUM, VALUE) FP_PP_ENUM_EXT_ENTRY_(FP_PP_NUM_ARGS VALUE, ENUM, VALUE)
#define DEFINE_EXT_ENUM(ENUM, ...) \
enum class ENUM { \
FP_PP_SEQ_FOR_EACH(FP_PP_ENUM_STD_ENTRY, ENUM, __VA_ARGS__) \
}; \
template<typename> \
struct enum_descriptor; \
\
template<> \
struct enum_descriptor<ENUM> { \
public: \
using enum_type = ENUM; \
using entry_type = ::fp::enum_entry<enum_type>; \
using this_type = enum_descriptor<enum_type>; \
using const_iterator = entry_type const *; \
using const_reverse_iterator = std::reverse_iterator<const_iterator>; \
using size_type = std::size_t; \
private: \
constexpr static std::size_t Size = FP_PP_NUM_ARGS(__VA_ARGS__); \
using container_type = std::array<entry_type, Size>; \
\
constexpr static container_type const _entries \
{ \
{ \
FP_PP_SEQ_FOR_EACH(FP_PP_ENUM_EXT_ENTRY, ENUM, __VA_ARGS__) \
} \
}; \
\
template<std::size_t... Is > \
constexpr static char const * name_of_impl(enum_type v, ::fp::indices<Is...>) { \
using std::get; \
return ::fp::enum_helper<enum_type>::get_name(v, get<Is>(_entries)...); \
} \
\
template<std::size_t... Is > \
constexpr static enum_type value_of_impl(char const * n, ::fp::indices<Is...>) { \
using std::get; \
return ::fp::enum_helper<enum_type>::get_value(n, get<Is>(_entries)...); \
} \
\
template<typename V, std::size_t... Is > \
static bool try_parse_impl(V val, enum_type & res, ::fp::indices<Is...>) { \
using std::get; \
return (::fp::enum_helper<enum_type>::is_valid_entry(val, get<Is>(_entries)...)) ? \
((res = static_cast<enum_type> (val)), true) \
: false; \
} \
\
template<typename V, std::size_t... Is > \
constexpr static enum_type parse_impl(V val, ::fp::indices<Is...>) { \
using std::get; \
return (::fp::enum_helper<enum_type>::parse(val, get<Is>(_entries)...)); \
} \
public: \
constexpr enum_descriptor() = default; \
enum_descriptor(enum_descriptor const &) = delete; \
enum_descriptor(enum_descriptor &&) = delete; \
\
constexpr static char const * name() noexcept { \
return #ENUM; \
} \
\
constexpr static char const * name_of(enum_type value) { \
return name_of_impl(value, ::fp::build_indices<Size>()); \
} \
\
constexpr static enum_type value_of(char const * name) { \
return value_of_impl(name, ::fp::build_indices<Size>()); \
} \
\
constexpr static size_type size() noexcept { \
return Size; \
} \
\
constexpr static const_iterator begin() { \
using std::get; \
return const_iterator(&get<0>(_entries)); \
} \
\
constexpr static const_iterator end() { \
using std::get; \
return const_iterator(&get<(Size - 1)>(_entries) + 1); \
} \
\
template<typename T, \
typename = typename std::enable_if<std::is_integral<T>::value>::type> \
static bool try_parse(T value, enum_type & res){ \
return try_parse_impl(value, res, ::fp::build_indices<Size>()); \
} \
\
template<typename T, \
typename = typename std::enable_if<std::is_integral<T>::value>::type> \
constexpr static enum_type parse(T value){ \
return parse_impl(value, ::fp::build_indices<Size>()); \
} \
}; \
template<> \
constexpr std::array<::fp::enum_entry<ENUM>, FP_PP_NUM_ARGS(__VA_ARGS__)> const enum_descriptor<ENUM>::_entries;
您可以在我的github上查看完整代码(包括示例)。
虽然这可以达到我的预期,但不能(直接)用作现有枚举的替代品。
为了节省大量编码,您需要支持的枚举应该如下定义:
DEFINE_EXT_ENUM(my_1st_enum, (fread, 3), (fwrite), (fflush, fread << 2));
DEFINE_EXT_ENUM(my_2nd_enum, (fopen), (fclose, 1));
最后一件事:不要尝试使用-Werr标志在GCC上编译它,因为编译会出错(我理解为什么,但我目前不知道如何解决它)。
编辑:
根据您的示例,您可以使用我的DEFINE_EXT_ENUM进行操作:
#include "enum_pp_def.hpp"
#include <cassert>
#include <iostream>
DEFINE_EXT_ENUM(turn, (right,1), (left,2));
DEFINE_EXT_ENUM(hand, (right,1), (left,2));
using turn_descr = enum_descriptor<turn>;
using hand_descr = enum_descriptor<hand>;
/* 2 */
constexpr char const * foo(turn t) {
return (turn_descr::is_valid((int)t)) ? (turn::right == t) ? "right turn" : "left turn" : throw t;
}
constexpr char const * foo(hand h) {
return (hand_descr::is_valid((int)h)) ? (hand::right == h) ? "right hand" : "left hand" : throw h;
}
/* End 2 */
int main(int argc, char ** argv) {
turn t1 = turn_descr::parse(1); // 3.a OK
turn t2(turn::right); // 3b. OK
hand h1(hand::left); // 3c. OK
hand h2 = hand_descr::parse(2); // 3d. OK
assert(t1 == turn::right); // 4. OK
/* 8 */
switch(t1) {
case turn::right:
std::cout << "right turn" << std::endl;
break;
case turn::left:
std::cout << "left turn" << std::endl;
break;
}
/* End 8 */
std::cout << foo(hand::left) << std::endl;
std::cout << foo(turn::right) << std::endl;
constexpr turn t3 = turn_descr::parse(3) // throw at compile time
turn t4 = turn_descr::parse(3); // throw at runtime
}
答案 4 :(得分:0)
我写了这个丑陋的宏来生成枚举包装(不支持C ++ 11枚举:()。 如您所见,ctor检查传递的值是否为枚举值:
#include <iostream>
using namespace std;
#define GENERATE_ENUM(NAME,VALUES...) class NAME \
{ \
private: \
static const int _ENUM_LOOKUP_TABLE[]; \
static const unsigned int _lenght; \
public: \
enum { __VA_ARGS__ }; \
\
NAME(int value) \
{ \
bool trying = true; \
\
for(int i = 0; i < _lenght ; ++i) \
trying = _ENUM_LOOKUP_TABLE[i] != value; \
\
if(trying) throw; \
} \
}; \
\
const int NAME::_ENUM_LOOKUP_TABLE[] = { __VA_ARGS__ }; \
const unsigned int NAME::_lenght = sizeof(NAME::_ENUM_LOOKUP_TABLE) / sizeof(int);
GENERATE_ENUM(MyEnum,ONE,TWO,THREE,FOUR,FIVE,SIX)
int main()
{
MyEnum e(33); //This throws an exception (Is not a value of the enum)
cout << MyEnum::THREE << endl; //Scoped enums, like C++11
cout << MyEnum::ONE << endl;
return 0;
}
对于此示例,CPP生成以下代码:
#include <iostream>
using namespace std;
class MyEnum
{
private:
static const int _ENUM_LOOKUP_TABLE[];
static const unsigned int _lenght;
public:
enum { ONE , TWO , THREE , FOUR , FIVE , SIX };
MyEnum (int value)
{
bool trying = false;
for(int i = 0; i < _lenght ; ++i)
trying = _ENUM_LOOKUP_TABLE[i] != value;
if(trying) throw;
}
};
const int MyEnum ::_ENUM_LOOKUP_TABLE[] = { ONE , TWO , THREE , FOUR , FIVE , SIX };
const unsigned int MyEnum ::_lenght = sizeof( MyEnum ::_ENUM_LOOKUP_TABLE) / sizeof(int);
int main()
{
MyEnum e(33); //This throws an exception (Is not a value of the enum)
cout << MyEnum::THREE << endl;
cout << MyEnum::ONE << endl;
return 0;
}
我认为这是最简单的宏解决方案,并且完全涵盖了您的问题。
答案 5 :(得分:0)
您在C ++ 11习惯用法中遇到过C ++ 03问题。
带有一个参数的“构造函数调用”符号X(2)
实际上是C样式转换(X) 2
的简写,后者又转换为有毒的reinterpret_cast< X >( 2 )
。范围枚举的卖点之一是它们不能与int
进行转换。
除了类类型之外,避免使用Type( initializer )
语法,并且不应该获得意外的值。 (嗯,如果海湾合作委员会对此有警告,那就太好了,但我找不到一个。)
在代码中替换“uniform initialization”X{ 2 }
会导致编译器抱怨正确的错误。
答案 6 :(得分:0)
我已经考虑了很多,它需要一个Core C ++语义添加。您要查找的内容在C ++中不可用,除了标准中定义的两种类型:bool
和nullptr_t
。
实际上,标准中明确指出类型bool
的对象(是的,我指的是标准中使用的对象,即标准中使用的名称)的值为true
或{ {1}}。 (在未定义行为范围之外)这意味着false
类型的(例如本地)变量在定义的行为领域中不能假定除bool
或true
之外的任何其他值,虽然它有更多价值的存储空间。 (比如简化为256)这是在语言层面上处理的,没有描述false
值的存储要求,而是有关bool
转换的要求。 (int
必须转换为false
且0
必须转换为true
)还要求1
位域必须正确转换为{{1} }}
实际上,这在C ++中还不存在。有一种方法来定义行为符合您希望的:1
类型实际上意味着(并允许)许多有趣的事情。 bool
类型包装齐全,拥挤到法定限制。任何值都可以转换为enum
类型,前提是它适合基础类型,并且可以安全地将其转换回int。而且你没有“免费存储空间”,也没有类型没有的值。
现在,我们假设此功能存在,并且它被称为enum
。您可以指定enum
映射到此:
explicit enum
由于只有两个值被“采用”,并且说“我想定义一个具有bool
和第三个值的类型:
explicit enum bool {
false,
true
};
然后,您将强烈键入这些类型的变量。您可以隐式地从bool
转换为explicit enum tristate {
using bool;
undefined
};
。您可以明确地从bool
转换为tristate
。编译器可以在tristate
变量上传播条件,编译器会知道bool
然后是tristate
,它是否已经对x != undefined && x != false
变量进行了处理。它还可以确定x == true
语句中不需要bool
个案,每次尝试向default
添加新值时都会恶意编译失败。
您必须禁止来自switch
的任意转化。
这是一份撰写并提交给委员会的提案,是对explicit enum
小组的讨论。