为了账号安全,请及时绑定邮箱和手机立即绑定

您正在使用C ++中的哪个Typesafe枚举?

您正在使用C ++中的哪个Typesafe枚举?

慕工程0101907 2019-10-25 15:10:05
众所周知,C ++中的内置枚举不是类型安全的。我想知道在那里使用了哪些实现类型安全枚举的类……我本人使用以下“自行车”,但是它有些冗长和有限:typesafeenum.h:struct TypesafeEnum{// Construction:public:    TypesafeEnum(): id (next_id++), name("") {}    TypesafeEnum(const std::string& n): id(next_id++), name(n) {}// Operations:public:    bool operator == (const TypesafeEnum& right) const;    bool operator != (const TypesafeEnum& right) const;    bool operator < (const TypesafeEnum& right) const;    std::string to_string() const { return name; }// Implementation:private:    static int next_id;    int id;    std::string name;};typesafeenum.cpp:int TypesafeEnum::next_id = 1;bool TypesafeEnum::operator== (const TypesafeEnum& right) const { return id == right.id; }bool TypesafeEnum::operator!= (const TypesafeEnum& right) const { return !operator== (right); }bool TypesafeEnum::operator< (const TypesafeEnum& right) const  { return id < right.id; }用法:class Dialog { ...    struct Result: public TypesafeEnum    {        static const Result CANCEL("Cancel");        static const Result OK("Ok");    };    Result doModal(); ...};const Dialog::Result Dialog::Result::OK;const Dialog::Result Dialog::Result::CANCEL;另外: 我认为我应该更具体地说明这些要求。我将尝试总结一下:优先级1:毫无例外,将枚举变量设置为无效值应该是不可能的(编译时错误)。优先级2:应该可以通过单个显式函数/方法调用将枚举值与int转换。优先级3:尽可能紧凑,优雅,方便的声明和使用优先级4:将枚举值与字符串进行相互转换。优先级5 :(很高兴)迭代枚举值的可能性。
查看完整描述

3 回答

?
德玛西亚99

TA贡献1770条经验 获得超3个赞

一个不错的折衷方法是这样的:


struct Flintstones {

   enum E {

      Fred,

      Barney,

      Wilma

   };

};


Flintstones::E fred = Flintstones::Fred;

Flintstones::E barney = Flintstones::Barney;

从版本上来说,它不是类型安全的,但是用法比标准枚举更好,并且在需要时仍可以利用整数转换。


查看完整回答
反对 回复 2019-10-25
?
千万里不及你

TA贡献1784条经验 获得超9个赞

我个人使用的是typesafe枚举用法的改编版本。它没有提供您在编辑中陈述的所有五个“要求”,但无论如何我还是非常不同意其中的一些。例如,我看不到Prio#4(将值转换为字符串)与类型安全有何关系。无论如何,大多数时间字符串表示形式的单个值都应与类型的定义分开(考虑一下i18n的简单原因)。Prio#5(迭代,它是可选的)是我想看到的枚举中自然发生的最好的事情之一,因此令您感到遗憾的是,它在您的请求中显示为“可选”,但似乎可以通过以下方式更好地解决一个单独的迭代系统,例如begin/end 函数或enum_iterator,这使它们可与STL和C ++ 11 foreach无缝协作。


OTOH这个简单的成语很好地提供了Prio#3 Prio#1,这是因为它实际上只会包装enum带有更多类型信息的。更不用说这是一个非常简单的解决方案,在很大程度上不需要任何外部依赖标头,因此很容易携带。它还具有使枚举范围为a-la-C ++ 11的优点:


// This doesn't compile, and if it did it wouldn't work anyway

enum colors { salmon, .... };

enum fishes { salmon, .... };


// This, however, works seamlessly.

struct colors_def { enum type { salmon, .... }; };

struct fishes_def { enum type { salmon, .... }; };


typedef typesafe_enum<colors_def> colors;

typedef typesafe_enum<fishes_def> fishes;

解决方案提供的唯一“漏洞”是,它无法解决无法阻止enum将不同类型的s(或an enum和int)直接进行比较的事实,因为直接使用值时会强制执行隐式转换至int:


if (colors::salmon == fishes::salmon) { .../* Ooops! */... }

但是到目前为止,我发现可以通过简单地与编译器进行更好的比较来解决这些问题,例如,显式提供一个可以比较任意两种不同enum类型的运算符,然后强制其失败:


// I'm using backports of C++11 utilities like static_assert and enable_if

template <typename Enum1, typename Enum2>

typename enable_if< (is_enum<Enum1>::value && is_enum<Enum2>::value) && (false == is_same<Enum1,Enum2>::value) , bool >

::type operator== (Enum1, Enum2) {

    static_assert (false, "Comparing enumerations of different types!");

}

尽管到目前为止似乎还没有破坏代码,并且确实可以不处理其他问题而明确地处理特定问题,但是我不确定这是“ 应该 ”做的事情(我怀疑它会干扰enum已参与其他地方声明的转换运算符;我很乐意收到对此的评论)。


将此与上面的类型安全习惯相结合,就可以enum class在操作性(可读性和可维护性)方面相对接近C ++ 11 ,而不必做任何过于晦涩的事情。而且我不得不承认这样做很有趣,我从没想过要问编译器是否在处理enums ...


查看完整回答
反对 回复 2019-10-25
  • 3 回答
  • 0 关注
  • 632 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信