goug
goug

Reputation: 2444

Using Qt's Q_DECLARE_FLAGS and Q_DECLARE_OPERATORS_FOR_FLAGS without Class Declaration

I have the following enum declaration and I'd like to make use of the QFlags support in Qt for extra type safety:

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)
}

This declaration works for methods where I declare a VisualAttributes parameter and pass an OR'd list of values, so that part is fine, but it breaks (apparently) everywhere that other flags are used such as the Qt::WindowFlags. The compilation error I'm getting is:

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

The issue seems to be with the Q_DECLARE_OPERATORS_FOR_FLAGS declaration; if I remove it, the compilation issues with other flag types are resolved, but since this declares the operators for the flags, then the compiler won't accept the OR'd list. Including the declaration is resulting in some kind of ambiguous definition, but I don't understand what it is.

The QFlags documentation shows an example of embedding the enum into a class declaration, and that not only seems cumbersome, but made a bigger mess than what I'm already dealing with. I also looked at Qt's flag declarations (for Qt::AlignmentFlag), and I don't see that they're doing anything different than I am in the code segment above.

Upvotes: 4

Views: 5641

Answers (2)

Parker Coates
Parker Coates

Reputation: 9418

This is actually a very old Qt bug, that was fixed in Qt 5.12. As a general rule, due to argument dependent lookup, custom operators should be declared in the same namespace as their arguments. Back when Qt first introduced these flag enums and operators they chose to declare them in the global namespace, possibly due to poor namespace or argument dependent lookup support in compilers at that time.

So if one is a good, modern C++ citizen and declares one's custom operator| in the same namespace as its arguments, lookup fails to find the Qt operator| when compiling code in that same namespace. It finds an operator| in the current namespace that doesn't match and finds no operator| via argument dependent lookup. Due to complicated reasons I can't explain, the lookup then stops instead of searching the global namespace, where it would find Qt's operator|.

You can see a very simplified example of this in action here.

So you have three options:

  1. Do what Qt (< 5.12) does and declare your custom operators in the global namespace, knowing that they might not be found by more modern C++ constructs.
  2. Do what C++ best practices recommend and place your custom operators in the same namespace as their arguments and sprinkle your code with using ::operator|; to make the Qt enum operators compile.
  3. Upgrade to Qt 5.12 or newer.

Upvotes: 4

goug
goug

Reputation: 2444

I was able to resolve this by moving the Q_DECLARE_OPERATORS_FOR_FLAGS declaration out of the namespace block, so it becomes:

Q_DECLARE_OPERATORS_FOR_FLAGS (ssp::VisualAttributes)

This resolved all of the compilation issues.

Upvotes: 4

Related Questions