如何使用c++ 11枚举类作为标志
How to use C++11 enum class for flags
假设我有这样一个类:
enum class Flags : char
{
FLAG_1 = 1;
FLAG_2 = 2;
FLAG_3 = 4;
FLAG_4 = 8;
};
现在我可以有一个变量,有类型标志,并分配一个值7
为例?我可以这样做吗:
Flags f = Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3;
或
Flags f = 7;
出现这个问题是因为在枚举中我没有为7
定义值。
您需要编写自己的重载operator|
(可能还有operator&
等)。
Flags operator|(Flags lhs, Flags rhs)
{
return static_cast<Flags>(static_cast<char>(lhs) | static_cast<char>(rhs));
}
将整数转换为枚举类型(无论是否有作用域)是定义良好的,只要该值在枚举值的范围内(否则为UB);[expr.static.cast]/p10)。对于具有固定基础类型的枚举(这包括所有作用域的枚举;[dcl.enum]/p5),枚举值的范围与底层类型([dcl.enum]/p8)的值的范围相同。如果底层类型不固定,规则会更棘手——所以不要这样做:)
使用std::underlying_type
而不是硬编码char
类型可能会更好。
Flags operator|(Flags lhs, Flags rhs) {
return static_cast<Flags>(
static_cast<std::underlying_type<Flags>::type>(lhs) |
static_cast<std::underlying_type<Flags>::type>(rhs)
);
}
现在,您可以更改枚举的基础类型,而无需在每次位操作符重载时更新该类型。
它应该处理任何枚举类型。我不确定它是否有任何副作用,并且是完全有效的c++代码。如果有什么问题,请告诉我。
template<class T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
constexpr T operator|(T lhs, T rhs)
{
return static_cast<T>(
static_cast<std::underlying_type<T>::type>(lhs) |
static_cast<std::underlying_type<T>::type>(rhs));
}
enum class
可能不是你需要的。
@T.C。向您展示了在指定基础类型的情况下如何做到这一点,但是您可能会遇到程序做一些不应该做的事情的地方。
一个例子是,您使用switch
并为每个定义的枚举值设置case
。
。
enum class my_enum: unsigned int{
first = 1,
second = 2,
third = 4,
fourth = 8
};
int main(){
auto e = static_cast<my_enum>(static_cast<unsigned int>(my_enum::first) | static_cast<unsigned int>(my_enum::second));
switch(e){
case my_enum::first:
case my_enum::second:
case my_enum::third:
case my_enum::fourth:
return 0;
}
std::cout << "Oh, no! You reached a part of the program you weren't meant to!n";
return 1;
}
将输出:
Oh, no! You reached a part of the program you weren't meant to!
则返回错误码1
。
这也是为什么你应该总是有一个default
案例的一个例子,当然,但这不是我的重点。
当然,你可以争辩说,只要enum class
的用户除了传递给函数外从不直接使用该值;这将是限制bitset值的好方法。但是我一直有点太信任,发现std::uint[n]_t
和一些constexpr
变量是最好的方法(如果用户设置了一个无效的位,它根本不做任何事情)。
您正在做的事情实际上并不适合enum class
,因为它违背了具有作用域枚举的目的。如果您将设置为未定义的值,则不能再枚举的值。
我知道这个问题有点老了,但我将写出一个我用来做这件事的方法。
(如果有的话,如果我将来再谷歌一下,我会把它记录下来,以便再次找到。)
我个人喜欢这个方法,因为智能感知(至少在VSCode版本…我没有Linux上的Visual Studio…)会自动发现你在做什么,并给你有用的提示。此外,它还避免了宏的使用,因此如果编译器不满意,它可以警告您。最后,如果没有注释,代码就不多了。您没有创建一个类并设置一堆重载或任何东西,但您仍然可以获得作用域枚举的好处,以便您可以为另一个枚举重用标志名称/值。不管怎样,回到这个例子。
namespace FlagsNS
{
/* This is an old/classic style enum so put it in a
namespace so that the names don't clash
(ie: you can define another enum with the values of
Flag_1 or Flag_2, etc... without it blowing up)
*/
enum Flags
{
Flag_1 = 1 << 0, //Same as 1
Flag_2 = 1 << 1, //Same as 2
Flag_3 = 1 << 2, //Same as 4
Flag_4 = 1 << 3 //Same as 8
};
}
/* This is telling the compiler you want a new "type" called Flags
but it is actually FlagsNS::Flags. This is sort of like using the
#define macro, except it doesn't use the preprocessor so the
compiler can give you warnings and errors.
*/
using Flags = FlagsNS::Flags;
//Later in code.... so int main() for example
int main()
{
//If you don't mind c-style casting
Flags flag = (Flags)(Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3);
//Or if you want to use c++ style casting
Flags flag = static_cast<Flags>(Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3);
//Check to see if flag has the FLAG_1 flag set.
if (flag & Flags::FLAG_1)
{
//This code works
}
}
问题代码无法编译。但是你可以这样做,
enum class Flags : char
{
FLAG_1 = 1,
FLAG_2 = 2,
FLAG_3 = 4,
FLAG_4 = 8,
};
int main() {
Flags f = static_cast<Flags>(7);
Flags f1 = static_cast<Flags>( static_cast<char>(Flags::FLAG_1) | static_cast<char>(Flags::FLAG_2) | static_cast<char>(Flags::FLAG_3) );
return 0;
}
,它工作
在这一点上,定义您自己的类来处理它可能是有意义的。
/** Warning: Untested code **/
struct Flag {
static Flag Flag_1;
static Flag Flag_2;
static Flag Flag_3;
static Flag Flag_4;
Flag operator = (Flag);
private:
char const value;
};
Flag operator | (Flag, Flag);
- 不带大括号的枚举形式
- 枚举环境变量的惯用C++14/C++17方法
- 类似枚举的计算常量
- 如何正确实现和访问运算符的各种自定义枚举器
- 尝试在枚举值中组合标志
- 为什么方法重载或枚举标志定义会触发 gcc7.2 编译器警告?
- 将 Qt::[枚举/标志] 注册为 QtMetaType
- C 很容易使用枚举进行标志和位操作
- 使用 Doxygen 或类似方法记录枚举标志
- Web 服务将标志作为文本值返回 - 如何转换为枚举
- 类型安全的c++11枚举类标志的模板
- 方便地为枚举定义按位运算符,以创建位标志
- 键入安全枚举位标志
- 如何使用枚举类作为一组标志
- 打印出枚举标志中的选定项目
- 如何使用c++ 11枚举类作为标志
- 枚举是实现位标志的规范方式吗?
- c++检查枚举标志
- C++枚举标志用法
- 应该如何将C位标志枚举转换为C++