在c#中,通过[flags]属性将枚举视为标志,但在c++中实现这一点的最佳方法是什么?
例如,我想写:
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
seahawk.flags = CanFly | EatsFish | Endangered;
然而,我得到编译器错误关于int/enum转换。除了生硬的角色转换,还有更好的表达方式吗?最好,我不想依赖第三方库(如boost或Qt)的构造。
编辑:如答案中所示,我可以通过声明seahawk来避免编译器错误。标记为int。但是,我希望有某种机制来执行类型安全,这样就不能编写seahawk了。flags = HasMaximizeButton。
下面是我的解决方案,不需要任何一堆重载或强制转换:
namespace EFoobar
{
enum
{
FB_A = 0x1,
FB_B = 0x2,
FB_C = 0x4,
};
typedef long Flags;
}
void Foobar(EFoobar::Flags flags)
{
if (flags & EFoobar::FB_A)
// do sth
;
if (flags & EFoobar::FB_B)
// do sth
;
}
void ExampleUsage()
{
Foobar(EFoobar::FB_A | EFoobar::FB_B);
EFoobar::Flags otherflags = 0;
otherflags|= EFoobar::FB_B;
otherflags&= ~EFoobar::FB_B;
Foobar(otherflags);
}
我认为这是可以的,因为我们无论如何都会识别(非强类型)枚举和整数。
只是作为一个(较长的)边注,如果你
要使用强类型枚举和
不需要重一点摆弄你的旗帜
性能不是问题
我会想到这个:
#include <set>
enum class EFoobarFlags
{
FB_A = 1,
FB_B,
FB_C,
};
void Foobar(const std::set<EFoobarFlags>& flags)
{
if (flags.find(EFoobarFlags::FB_A) != flags.end())
// do sth
;
if (flags.find(EFoobarFlags::FB_B) != flags.end())
// do sth
;
}
void ExampleUsage()
{
Foobar({EFoobarFlags::FB_A, EFoobarFlags::FB_B});
std::set<EFoobarFlags> otherflags{};
otherflags.insert(EFoobarFlags::FB_B);
otherflags.erase(EFoobarFlags::FB_B);
Foobar(otherflags);
}
使用c++ 11初始化列表和枚举类。
我发现自己也在问同样的问题,并提出了一个基于c++ 11的通用解决方案,类似于soru的方案:
template <typename TENUM>
class FlagSet {
private:
using TUNDER = typename std::underlying_type<TENUM>::type;
std::bitset<std::numeric_limits<TUNDER>::max()> m_flags;
public:
FlagSet() = default;
template <typename... ARGS>
FlagSet(TENUM f, ARGS... args) : FlagSet(args...)
{
set(f);
}
FlagSet& set(TENUM f)
{
m_flags.set(static_cast<TUNDER>(f));
return *this;
}
bool test(TENUM f)
{
return m_flags.test(static_cast<TUNDER>(f));
}
FlagSet& operator|=(TENUM f)
{
return set(f);
}
};
界面可以根据口味进行改进。那么它可以这样使用:
FlagSet<Flags> flags{Flags::FLAG_A, Flags::FLAG_C};
flags |= Flags::FLAG_D;