• Q_ENUM Q_ENUMS Q_ENUM_NS Q_FLAG Q_FLAGS Q_FLAG_NS


    使用qt5.12.0

    注意:qtcreator对qt程序的编译过程是先qmake然后构建(等价于make)。从项目->build->构建步骤可以看出,构建过程中先用qmake生成makefile,make过程用jom和前面产生的makefile生成目标程序的exe。jom按makefile规则的运行过程中根据依赖会先调用moc.exe生成moc_*.cpp文件,然后是按正常C++编译过程进行C++预编译(宏替换),再然后是C++编译。也就是说moc生成moc_*.cpp代码是在C++编译器进行预编译之前!jom具体如何调用moc的过程请参考:
    qt 工程构建过程 默认构建路径设置 通过Dos窗口运行命令编译qt工程_丘上人的博客-CSDN博客_qt执行dos命令

    qt中有一套meta系统,用于方便做类型检测、反射等等操作。

    Q_ENUMS是最早的将enum类型纳入meta系统的宏。在qt5.5版(网上看到的)之后,主要使用Q_ENUM宏。

    Q_ENUM、Q_ENUMS、Q_ENUM_NS、Q_FLAG、Q_FLAGS、Q_FLAG_NS这些宏在qt中有两重意义,一重意义是直观的作为C++的宏定义,另一重意义是作为Qt的关键字。
    Q_ENUMS宏定义如下:

    1. //QtInstallDir\Src\qtbase\src\corelib\kernel\qobjectdefs.h
    2. #ifndef QT_ANNOTATE_CLASS
    3. # ifndef Q_COMPILER_VARIADIC_MACROS
    4. # define QT_ANNOTATE_CLASS(type, x)
    5. # else
    6. # define QT_ANNOTATE_CLASS(type, ...)
    7. # endif
    8. ........
    9. #define Q_ENUMS(x) QT_ANNOTATE_CLASS(qt_enums, x)

    作为宏定义,Q_ENUMS基本没有意义。

    而作为Qt的关键字(参考:Q_PLUGIN_METADATA_丘上人的博客-CSDN博客 ),在moc.exe中经过Preprocessor对象的识别和Moc对象的处理并在Moc对象中记录下相关信息,然后在Generator对象中根据相关信息生成moc_*.cpp中enum相关的必要的代码。

    1. //QtInstallDir\Src\qtbase\src\tools\moc\util\generate_keywords.cpp
    2. static const Keyword keywords[] = {
    3. ............
    4. { "Q_ENUMS", "Q_ENUMS_TOKEN" },
    5. { "Q_ENUM", "Q_ENUM_TOKEN" },
    6. { "Q_ENUM_NS", "Q_ENUM_NS_TOKEN" },
    7. { "Q_FLAGS", "Q_FLAGS_TOKEN" },
    8. { "Q_FLAG", "Q_FLAG_TOKEN" },
    9. { "Q_FLAG_NS", "Q_FLAG_NS_TOKEN" },
    10. ............
    11. }
    12. ----------------------
    13. //QtInstallDir\Src\qtbase\src\tools\moc\preprocessor.cpp
    14. Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocessor::TokenizeMode mode)
    15. {
    16. ..........
    17. while (*data) {
    18. if (mode == TokenizeCpp || mode == TokenizeDefine) {
    19. int column = 0;
    20. const char *lexem = data;
    21. int state = 0;
    22. Token token = NOTOKEN;
    23. for (;;) {
    24. if (static_cast<signed char>(*data) < 0) {
    25. ++data;
    26. continue;
    27. }
    28. int nextindex = keywords[state].next;
    29. int next = 0;
    30. if (*data == keywords[state].defchar)
    31. next = keywords[state].defnext;
    32. else if (!state || nextindex)
    33. next = keyword_trans[nextindex][(int)*data];
    34. if (!next)
    35. break;
    36. state = next;
    37. token = keywords[state].token;
    38. ++data;
    39. }
    40. // suboptimal, is_ident_char should use a table
    41. if (keywords[state].ident && is_ident_char(*data))
    42. token = keywords[state].ident;
    43. if (token == NOTOKEN) {
    44. if (*data)
    45. ++data;
    46. // an error really, but let's ignore this input
    47. // to not confuse moc later. However in pre-processor
    48. // only mode let's continue.
    49. if (!Preprocessor::preprocessOnly)
    50. continue;
    51. }
    52. ..........
    53. }
    54. ----------------------
    55. //QtInstallDir\Src\qtbase\src\tools\moc\moc.cpp
    56. void Moc::parse(){
    57. .........
    58. case Q_ENUMS_TOKEN:
    59. case Q_ENUM_TOKEN:
    60. parseEnumOrFlag(&def, false);
    61. break;
    62. case Q_ENUM_NS_TOKEN:
    63. error("Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead");
    64. break;
    65. case Q_FLAGS_TOKEN:
    66. case Q_FLAG_TOKEN:
    67. parseEnumOrFlag(&def, true);
    68. ........
    69. }
    70. void Moc::parseEnumOrFlag(BaseDef *def, bool isFlag)
    71. {
    72. next(LPAREN);
    73. QByteArray identifier;
    74. while (test(IDENTIFIER)) {
    75. identifier = lexem();
    76. while (test(SCOPE) && test(IDENTIFIER)) {
    77. identifier += "::";
    78. identifier += lexem();
    79. }
    80. def->enumDeclarations[identifier] = isFlag;
    81. }
    82. next(RPAREN);
    83. }
    84. ----------------------
    85. //QtInstallDir\Src\qtbase\src\tools\moc\generator.cpp
    86. void Generator::generateEnums(int index)
    87. {
    88. if (cdef->enumDeclarations.isEmpty())
    89. return;
    90. fprintf(out, "\n // enums: name, alias, flags, count, data\n");
    91. index += 5 * cdef->enumList.count();
    92. int i;
    93. for (i = 0; i < cdef->enumList.count(); ++i) {
    94. const EnumDef &e = cdef->enumList.at(i);
    95. int flags = 0;
    96. if (cdef->enumDeclarations.value(e.name))
    97. flags |= EnumIsFlag;
    98. if (e.isEnumClass)
    99. flags |= EnumIsScoped;
    100. fprintf(out, " %4d, %4d, 0x%.1x, %4d, %4d,\n",
    101. stridx(e.name),
    102. e.enumName.isNull() ? stridx(e.name) : stridx(e.enumName),
    103. flags,
    104. e.values.count(),
    105. index);
    106. index += e.values.count() * 2;
    107. }
    108. fprintf(out, "\n // enum data: key, value\n");
    109. for (i = 0; i < cdef->enumList.count(); ++i) {
    110. const EnumDef &e = cdef->enumList.at(i);
    111. for (int j = 0; j < e.values.count(); ++j) {
    112. const QByteArray &val = e.values.at(j);
    113. QByteArray code = cdef->qualified.constData();
    114. if (e.isEnumClass)
    115. code += "::" + (e.enumName.isNull() ? e.name : e.enumName);
    116. code += "::" + val;
    117. fprintf(out, " %4d, uint(%s),\n",
    118. stridx(val), code.constData());
    119. }
    120. }
    121. }

    主要来关注一下Q_ENUMS的使用和moc_*.cpp中生成的enum相关的代码:
     

    1. //MyProjectPath/myenum.h
    2. class MyEnum : public QObject
    3. {
    4. Q_OBJECT
    5. public:
    6. explicit MyEnum(QObject *parent = nullptr);
    7. enum class Orientation
    8. {
    9. Up = 1,
    10. Down = 2,
    11. Left = 4,
    12. Right = 8,
    13. };
    14. Q_ENUMS(Orientation) //如不使用Orientation,可省略
    15. };
    16. ------------------
    17. //moc_myenum.cpp
    18. struct qt_meta_stringdata_MyEnum_t {
    19. QByteArrayData data[6];
    20. char stringdata0[38];
    21. };
    22. static const qt_meta_stringdata_MyEnum_t qt_meta_stringdata_MyEnum = {
    23. {
    24. QT_MOC_LITERAL(0, 0, 6), // "MyEnum"
    25. QT_MOC_LITERAL(1, 7, 11), // "Orientation"
    26. QT_MOC_LITERAL(2, 19, 2), // "Up"
    27. QT_MOC_LITERAL(3, 22, 4), // "Down"
    28. QT_MOC_LITERAL(4, 27, 4), // "Left"
    29. QT_MOC_LITERAL(5, 32, 5) // "Right"
    30. },
    31. "MyEnum\0Orientation\0Up\0Down\0Left\0Right"
    32. };
    33. static const uint qt_meta_data_MyEnum[] = {
    34. // content:
    35. 8, // revision
    36. 0, // classname
    37. 0, 0, // classinfo
    38. 0, 0, // methods
    39. 0, 0, // properties
    40. 1, 14, // enums/sets
    41. 0, 0, // constructors
    42. 0, // flags
    43. 0, // signalCount
    44. // enums: name, alias, flags, count, data
    45. 1, 1, 0x2, 4, 19,
    46. // enum data: key, value
    47. 2, uint(MyEnum::Orientation::Up),
    48. 3, uint(MyEnum::Orientation::Down),
    49. 4, uint(MyEnum::Orientation::Left),
    50. 5, uint(MyEnum::Orientation::Right),
    51. 0 // eod
    52. };
    53. QT_INIT_METAOBJECT const QMetaObject MyEnum::staticMetaObject = { {
    54. &QObject::staticMetaObject,
    55. qt_meta_stringdata_MyEnum.data,
    56. qt_meta_data_MyEnum,
    57. qt_static_metacall,
    58. nullptr,
    59. nullptr
    60. } };

    可以看到所有的enum class Orientation的信息都存入到了qt_meta_stringdata_MyEnum和qt_meta_data_MyEnum中了 。qt_meta_stringdata_MyEnum主要记录名字,最关键的还是看qt_meta_data_MyEnum,记录了enum的数量和enum的具体类型信息。其实到此为止,meta系统就已经完成了初始化了,并将初始化信息作为这几个数组中记录的数据固定存储下来了

    程序运行起来后,先将moc_*.cpp中的几个static类型的数据初始化了,也就是说MyEnum类相关的qt_meta_stringdata_MyEnum和qt_meta_data_MyEnum、QMetaObject MyEnum::staticMetaObject三个static类型的变量在main函数运行之前就是被初始化了,后续所有的对该类的meta系统的调用都是基于这几个static类型数据进行运算的。

    而具体的调用过程可以参看下面这篇文章:QMetaObjectPrivate meta Q_INVOKABLE 

    所以总结下:Q_ENUMS宏具体意义就是将enum class Orientation{...}的所有信息包括具体名字在moc.exe的操作下存入meta系统中。

    1. //QtInstallDir\Src\qtbase\src\corelib\kernel\qmetaobject.h
    2. class Q_CORE_EXPORT QMetaEnum //将枚举类型中的名字和值进行转换的工具类。
    3. {
    4. public:
    5. Q_DECL_CONSTEXPR inline QMetaEnum() : mobj(nullptr), handle(0) {}
    6. const char *name() const;
    7. const char *enumName() const;
    8. bool isFlag() const;
    9. bool isScoped() const;
    10. int keyCount() const;
    11. const char *key(int index) const;
    12. int value(int index) const;
    13. const char *scope() const;
    14. int keyToValue(const char *key, bool *ok = nullptr) const;
    15. const char* valueToKey(int value) const;
    16. int keysToValue(const char * keys, bool *ok = nullptr) const;
    17. QByteArray valueToKeys(int value) const;
    18. inline const QMetaObject *enclosingMetaObject() const { return mobj; }
    19. inline bool isValid() const { return name() != nullptr; }
    20. .....
    21. }
    22. ----------------------------
    23. //projectPath//main.cpp
    24. static QString getStringByID(MyEnum::Orientation id)
    25. {
    26. if(id<MyEnum::Orientation::Up||id>MyEnum::Orientation::Right)
    27. {
    28. return QString();
    29. }
    30. const QMetaObject obj=MyEnum::staticMetaObject;//第二步
    31. int index=obj.indexOfEnumerator("Orientation");//第三步
    32. if(index<0)
    33. {
    34. return QString();
    35. }
    36. QMetaEnum en= obj.enumerator(index);//第四步,拿到MyEnum中的Orientation的QMetaEnum对象
    37. return QString(en.valueToKey(static_cast<int>(id)));//返回枚举的字符串
    38. }
    39. int main()
    40. {
    41. qDebug()<<getStringByID(MyEnum::Orientation::Up)<<endl;//输出:Up
    42. return 0;
    43. }

    Q_ENUM与Q_ENUMS相比,多了一个宏定义,但是使用时省去了一些操作:

    1. //QtInstallDir\Src\qtbase\src\corelib\kernel\qobjectdefs.h
    2. #define Q_ENUM_IMPL(ENUM) \
    3. friend Q_DECL_CONSTEXPR const QMetaObject *qt_getEnumMetaObject(ENUM) Q_DECL_NOEXCEPT { return &staticMetaObject; } \
    4. friend Q_DECL_CONSTEXPR const char *qt_getEnumName(ENUM) Q_DECL_NOEXCEPT { return #ENUM; }
    5. #define Q_ENUM(x) Q_ENUMS(x) Q_ENUM_IMPL(x)
    6. ------------------------------------
    7. //QtInstallDir\Src\qtbase\src\corelib\kernel\qmetaobject.h
    8. class Q_CORE_EXPORT QMetaEnum //将枚举类型中的名字和值进行转换的工具类。
    9. {
    10. ...........
    11. template<typename T> static QMetaEnum fromType() {
    12. Q_STATIC_ASSERT_X(QtPrivate::IsQEnumHelper<T>::Value,
    13. "QMetaEnum::fromType only works with enums declared as Q_ENUM or Q_FLAG");
    14. const QMetaObject *metaObject = qt_getEnumMetaObject(T());
    15. const char *name = qt_getEnumName(T());
    16. return metaObject->enumerator(metaObject->indexOfEnumerator(name));
    17. }
    18. .......
    19. }
    20. -----------------------------------
    21. //myProjectPath/main.cpp
    22. int main()
    23. {
    24. qDebug()<<getStringByID(MyEnum::Orientation::Up)<<endl;//输出:Up
    25. return 0;
    26. }

     Q_ENUM_NS 是用于结合Q_NAMESPACE来使用的。作为C++宏定义与Q_ENUM是一致的。作为Qt关键字稍微有点差别。具体使用可以参考Qt中的枚举变量,Q_ENUM,Q_FLAG,Q_NAMESPACE,Q_ENUM_NS,Q_FLAG_NS以及其他_荆楚闲人的博客-CSDN博客_qt 定义枚举

    1. //QtInstallDir\Src\qtbase\src\corelib\kernel\qobjectdefs.h
    2. #define Q_ENUM_NS_IMPL(ENUM) \
    3. inline Q_DECL_CONSTEXPR const QMetaObject *qt_getEnumMetaObject(ENUM) Q_DECL_NOEXCEPT { return &staticMetaObject; } \
    4. inline Q_DECL_CONSTEXPR const char *qt_getEnumName(ENUM) Q_DECL_NOEXCEPT { return #ENUM; }
    5. #define Q_ENUM_NS(x) Q_ENUMS(x) Q_ENUM_NS_IMPL(x)
    6. #define Q_NAMESPACE \
    7. extern const QMetaObject staticMetaObject; \
    8. QT_ANNOTATE_CLASS(qt_qnamespace, "") \
    9. /*end*/

    QMetaObjectPrivate meta_constractors Q_INVOKABLE_丘上人的博客-CSDN博客 Q_FLAGS,其意义包含了Q_ENUMS的意义,也就是Q_ENUMS能做的Q_FLAGS也能做。在moc.exe中Q_FLAGS与Q_ENUMS的操作是一致的,两者作为C++宏定义的意义也是一样的。Q_FLAGS和Q_FLAG的差异 与 Q_ENUMS和Q_ENUM的差异是一致的,为了方便下面就讲Q_FLAG

    Q_FLAG通过Q_DECLARE_FLAGS宏引入了QFlags模板类,它将enum 转换成QFlags类,从而进行使enum类型数据进行一些位相关的运算并能成功返回其名称。

    1. //projectPath/myEnum.h
    2. class MyEnum : public QObject
    3. {
    4. Q_OBJECT
    5. public:
    6. explicit MyEnum(QObject *parent = nullptr);
    7. enum class Orientation
    8. {
    9. Up = 1,
    10. Down = 2,
    11. Left = 4,
    12. Right = 8,
    13. };
    14. Q_FLAGS(Orientation) //如不使用Orientation,可省略
    15. Q_DECLARE_FLAGS(OrientationFlags, Orientation)
    16. };
    17. Q_DECLARE_OPERATORS_FOR_FLAGS(MyEnum::OrientationFlags)
    18. -----------------------------
    19. //moc_myenum.cpp
    20. ........
    21. static const qt_meta_stringdata_MyEnum_t qt_meta_stringdata_MyEnum = {
    22. {
    23. QT_MOC_LITERAL(0, 0, 6), // "MyEnum"
    24. QT_MOC_LITERAL(1, 7, 11), // "Orientation"
    25. QT_MOC_LITERAL(2, 19, 2), // "Up"
    26. QT_MOC_LITERAL(3, 22, 4), // "Down"
    27. QT_MOC_LITERAL(4, 27, 4), // "Left"
    28. QT_MOC_LITERAL(5, 32, 5) // "Right"
    29. },
    30. "MyEnum\0Orientation\0Up\0Down\0Left\0Right"
    31. };
    32. .......
    33. static const uint qt_meta_data_MyEnum[] = {
    34. // content:
    35. 8, // revision
    36. 0, // classname
    37. 0, 0, // classinfo
    38. 0, 0, // methods
    39. 0, 0, // properties
    40. 1, 14, // enums/sets
    41. 0, 0, // constructors
    42. 0, // flags
    43. 0, // signalCount
    44. // enums: name, alias, flags, count, data
    45. 1, 1, 0x3, 4, 19,
    46. // enum data: key, value
    47. 2, uint(MyEnum::Orientation::Up),
    48. 3, uint(MyEnum::Orientation::Down),
    49. 4, uint(MyEnum::Orientation::Left),
    50. 5, uint(MyEnum::Orientation::Right),
    51. 0 // eod
    52. };
    53. ......
    54. -----------------------------
    55. //QtInstallPath\Src\qtbase\src\corelib\global\qflags.h
    56. template<typename Enum>
    57. class QFlags
    58. {
    59. Q_STATIC_ASSERT_X((sizeof(Enum) <= sizeof(int)),
    60. "QFlags uses an int as storage, so an enum with underlying "
    61. "long long will overflow.");
    62. Q_STATIC_ASSERT_X((std::is_enum<Enum>::value), "QFlags is only usable on enumeration types.");
    63. struct Private;
    64. typedef int (Private::*Zero);
    65. template <typename E> friend QDataStream &operator>>(QDataStream &, QFlags &);
    66. template <typename E> friend QDataStream &operator<<(QDataStream &, QFlags<E>);
    67. public:
    68. #if defined(Q_CC_MSVC) || defined(Q_CLANG_QDOC)
    69. // see above for MSVC
    70. // the definition below is too complex for qdoc
    71. typedef int Int;
    72. #else
    73. typedef typename std::conditional<
    74. std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
    75. unsigned int,
    76. signed int
    77. >::type Int;
    78. #endif
    79. typedef Enum enum_type;
    80. // compiler-generated copy/move ctor/assignment operators are fine!
    81. #ifdef Q_CLANG_QDOC
    82. Q_DECL_CONSTEXPR inline QFlags(const QFlags &other);
    83. Q_DECL_CONSTEXPR inline QFlags &operator=(const QFlags &other);
    84. #endif
    85. Q_DECL_CONSTEXPR inline QFlags(Enum flags) Q_DECL_NOTHROW : i(Int(flags)) {}
    86. Q_DECL_CONSTEXPR inline QFlags(Zero = Q_NULLPTR) Q_DECL_NOTHROW : i(0) {}
    87. Q_DECL_CONSTEXPR inline QFlags(QFlag flag) Q_DECL_NOTHROW : i(flag) {}
    88. #ifdef Q_COMPILER_INITIALIZER_LISTS
    89. Q_DECL_CONSTEXPR inline QFlags(std::initializer_list<Enum> flags) Q_DECL_NOTHROW
    90. : i(initializer_list_helper(flags.begin(), flags.end())) {}
    91. #endif
    92. Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator&=(int mask) Q_DECL_NOTHROW { i &= mask; return *this; }
    93. Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator&=(uint mask) Q_DECL_NOTHROW { i &= mask; return *this; }
    94. Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator&=(Enum mask) Q_DECL_NOTHROW { i &= Int(mask); return *this; }
    95. Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator|=(QFlags other) Q_DECL_NOTHROW { i |= other.i; return *this; }
    96. Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator|=(Enum other) Q_DECL_NOTHROW { i |= Int(other); return *this; }
    97. Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator^=(QFlags other) Q_DECL_NOTHROW { i ^= other.i; return *this; }
    98. Q_DECL_RELAXED_CONSTEXPR inline QFlags &operator^=(Enum other) Q_DECL_NOTHROW { i ^= Int(other); return *this; }
    99. Q_DECL_CONSTEXPR inline operator Int() const Q_DECL_NOTHROW { return i; }
    100. Q_DECL_CONSTEXPR inline QFlags operator|(QFlags other) const Q_DECL_NOTHROW { return QFlags(QFlag(i | other.i)); }
    101. Q_DECL_CONSTEXPR inline QFlags operator|(Enum other) const Q_DECL_NOTHROW { return QFlags(QFlag(i | Int(other))); }
    102. Q_DECL_CONSTEXPR inline QFlags operator^(QFlags other) const Q_DECL_NOTHROW { return QFlags(QFlag(i ^ other.i)); }
    103. Q_DECL_CONSTEXPR inline QFlags operator^(Enum other) const Q_DECL_NOTHROW { return QFlags(QFlag(i ^ Int(other))); }
    104. Q_DECL_CONSTEXPR inline QFlags operator&(int mask) const Q_DECL_NOTHROW { return QFlags(QFlag(i & mask)); }
    105. Q_DECL_CONSTEXPR inline QFlags operator&(uint mask) const Q_DECL_NOTHROW { return QFlags(QFlag(i & mask)); }
    106. Q_DECL_CONSTEXPR inline QFlags operator&(Enum other) const Q_DECL_NOTHROW { return QFlags(QFlag(i & Int(other))); }
    107. Q_DECL_CONSTEXPR inline QFlags operator~() const Q_DECL_NOTHROW { return QFlags(QFlag(~i)); }
    108. Q_DECL_CONSTEXPR inline bool operator!() const Q_DECL_NOTHROW { return !i; }
    109. Q_DECL_CONSTEXPR inline bool testFlag(Enum flag) const Q_DECL_NOTHROW { return (i & Int(flag)) == Int(flag) && (Int(flag) != 0 || i == Int(flag) ); }
    110. Q_DECL_RELAXED_CONSTEXPR inline QFlags &setFlag(Enum flag, bool on = true) Q_DECL_NOTHROW
    111. {
    112. return on ? (*this |= flag) : (*this &= ~Int(flag));
    113. }

     另外还有一个宏:Q_DECLARE_OPERATORS_FOR_FLAGS
    其意义是让"|"操作更方便。
     

    1. //QtInstallPath\Src\qtbase\src\corelib\global\qflags.h
    2. #define Q_DECLARE_OPERATORS_FOR_FLAGS(Flags) \
    3. Q_DECL_CONSTEXPR inline QFlags<Flags::enum_type> operator|(Flags::enum_type f1, Flags::enum_type f2) Q_DECL_NOTHROW \
    4. { return QFlags<Flags::enum_type>(f1) | f2; } \
    5. Q_DECL_CONSTEXPR inline QFlags<Flags::enum_type> operator|(Flags::enum_type f1, QFlags<Flags::enum_type> f2) Q_DECL_NOTHROW \
    6. { return f2 | f1; } Q_DECLARE_INCOMPATIBLE_FLAGS(Flags)
    7. ---------------------------------
    8. int main()
    9. {
    10. MyEnum::Orientation o = MyEnum::Orientation::Up;
    11. MyEnum::OrientationFlags s = o;
    12. MyEnum::OrientationFlags s1 = s|o;
    13. //如果没有声明Q_DECLARE_OPERATORS_FOR_FLAGS(MyEnum::OrientationFlags),下面语句将会报错。
    14. MyEnum::OrientationFlags s2 = o|s;
    15. return 0;
    16. }

  • 相关阅读:
    mac 中配置idea自带maven环境变量
    [b01lers2020]Life on Mars (难发现的sql注入)
    虎哥说车和亚洲小姐全球总冠军吴丹跨界影视圈相遇《猎枭生死线》
    【Docker系列】Docker生产常用命令01
    tiup dm audit
    椭圆曲线算法
    [系统安全] 五十三.DataCon竞赛 (2)2022年DataCon涉网分析之恶意样本IOC自动化提取数据集详解
    QT 之QStringList
    R-CNN->各个阶段是独立的
    海康Visionmaster-全局脚本:通过通讯触发快速匹配 模块换型的方法
  • 原文地址:https://blog.csdn.net/qiushangren/article/details/126681992