在没有类声明的情况下使用Qt的Q_DECLARE_FLAGS和Q_DECLARE_OPERATORS_FOR_FLAGS

时间:2016-09-13 20:10:59

标签: c++ qt

我有以下枚举声明,我想在Qt中使用QFlags支持以获得额外的类型安全性:

namespace ssp
{
    enum VisualAttribute
    {
        AttrBrushColor     = 0x001,
        AttrBrushTexture   = 0x002,
        AttrPenCapStyle    = 0x004,
        AttrPenColor       = 0x008,
        AttrPenJoinStyle   = 0x010,
        AttrPenPattern     = 0x020,
        AttrPenScalable    = 0x040,
        AttrPenWidth       = 0x080,
        AttrSymbolColor    = 0x100,
        AttrTextColor      = 0x200,
        AttrTextFontFamily = 0x400,
        AttrTextHeight     = 0x800,
        AttrAllFlags       = 0xfff
    };

    Q_DECLARE_FLAGS (VisualAttributes, VisualAttribute)
    Q_DECLARE_OPERATORS_FOR_FLAGS (VisualAttributes)
}

此声明适用于我声明VisualAttributes参数并传递OR的值列表的方法,因此该部分很好,但它(显然)在使用其他标志(如Qt :: WindowFlags)的任何地方中断(显然)。我得到的编译错误是:

error C2664: 'void QWidget::setWindowFlags(Qt::WindowFlags)' : cannot convert argument 1 from 'int' to 'Qt::WindowFlags'
No constructor could take the source type, or constructor overload resolution was ambiguous

问题似乎与Q_DECLARE_OPERATORS_FOR_FLAGS声明有关;如果我删除它,解决了其他标志类型的编译问题,但由于这声明了标志的运算符,编译器将不接受OR'd列表。包含声明会产生某种模棱两可的定义,但我不明白它是什么。

QFlags文档显示了将枚举嵌入到类声明中的示例,这不仅看起来很麻烦,而且比我已经处理的更糟糕。我还查看了Qt的标志声明(对于Qt :: AlignmentFlag),我没有看到他们在上面的代码段中做了不同的事情。

2 个答案:

答案 0 :(得分:2)

我能够通过将Q_DECLARE_OPERATORS_FOR_FLAGS声明移出命名空间块来解决此问题,因此它变为:

Q_DECLARE_OPERATORS_FOR_FLAGS (ssp::VisualAttributes)

这解决了所有编译问题。

答案 1 :(得分:2)

这实际上是一个(非常古老的)Qt错误。通常,由于argument dependent lookup,自定义运算符应在与其参数相同的命名空间中声明。当Qt首次引入这些标志枚举和运算符时,他们选择在全局名称空间中声明它们,可能是由于当时编译器对名称空间或依赖于参数的查找支持不佳。据推测,移动它们会破坏一些现有代码,因此它们会留在原处。 (也许这是Qt 6的考虑因素?)

因此,如果一个人是一个良好的,现代的C ++公民,并且在与其参数相同的名称空间中声明其自定义operator|,则在同一名称空间中编译代码时,查找将找不到Qt operator|。它在当前名称空间中找到不匹配的operator|,并且通过依赖于参数的查找找不到operator|。由于我无法解释的复杂原因,查找然后停止,而不是搜索将找到Qt的operator|的全局名称空间。

因此,您有两种选择:

  1. 执行Qt的操作,并在全局命名空间中声明您的自定义运算符,因为他们知道更现代的C ++构造可能找不到它们。
  2. 执行C ++最佳实践的建议,然后将自定义运算符与自变量放在其参数相同的名称空间中,并在代码中加上using ::operator|;,以使Qt枚举运算符得以编译。