Q_DECLARE_OPERATORS_FOR_FLAGS: also define operator&

Bitwise ANDing an enum and a QFlags should yield identical results
as ANDing a QFlags and the enum. This latter operator already existed,
so add support for the former.

Plus, ANDing two enumerators of a flag type should yield a QFlags, and
not a plain int. (Note that an arbitrary amount of bits can be set
in the result of such operation, as the enumerators themselves may be
masks with more than 1 bit set.)

This is slightly source incompatible, as we're changing the return
type of operator&. Code that was assigning that result to `int` still
works as expected, but code that wanted the value back in a QFlags may
have inserted some conversions like:

  flag = Enum(enumerator & otherFlag);
              ^---------------------
                used to yield int, now QFlags

which are now going to break as there's no QFlags->Enum conversion.

An earlier attempt of this patch, introduced -- and immediately
deprecated -- such a conversion, under the form of

  explicit operator Enum() const;

in order to keep such old code working both before and after this
change. The problem is that MSVC has a bug with explicit conversions to
enumerations [1], which get accidentally considered when they shouldn't,
and thus making a lot of code stop compiling due to (false) ambiguities.
So, I'm not adding that operator any more.

Note that there's a way to keep code working before and after this
change: just use

  flag = Flags(enumerator & otherFlag); // Flags, not Enum

(thanks Edward), which goes through a int -> QFlag -> QFlags conversion,
and arguably is the idiomatic way of writing such code; or use

  flag = otherFlag & enumerator;

which, as discussed above, has always returned a QFlags.

[1] https://developercommunity2.visualstudio.com/t/explicit-conversion-operator-to-enum-is/1412616

[ChangeLog][Potentially Source-Incompatible Changes][QFlags] Bitwise
AND-ing two values of an enum type for which flag operations have been
declared now produces a QFlags object. Similarly, AND-ing a value
of enum type and one of type QFlags now produces a QFlags object.
Before, it produced an integer in both cases. This may introduce a
slight incompatibility in user code; it is possible to keep the code
working in Qt versions before and after this change by inserting
enough conversions to QFlags, and/or by changing expressions such as
`enum & flag` to `flag & enum` (the latter has always produced a QFlags
object).

Change-Id: I6e645c010d3aa677820545b0c25678f1dc9a3295
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Giuseppe D'Angelo 2021-05-02 01:37:32 +02:00
parent d2759e0e32
commit 2be9e6cc26

View File

@ -172,6 +172,10 @@ constexpr inline QFlags<Flags::enum_type> operator|(Flags::enum_type f1, Flags::
{ return QFlags<Flags::enum_type>(f1) | f2; } \
constexpr inline QFlags<Flags::enum_type> operator|(Flags::enum_type f1, QFlags<Flags::enum_type> f2) noexcept \
{ return f2 | f1; } \
constexpr inline QFlags<Flags::enum_type> operator&(Flags::enum_type f1, Flags::enum_type f2) noexcept \
{ return QFlags<Flags::enum_type>(f1) & f2; } \
constexpr inline QFlags<Flags::enum_type> operator&(Flags::enum_type f1, QFlags<Flags::enum_type> f2) noexcept \
{ return f2 & f1; } \
constexpr inline void operator+(Flags::enum_type f1, Flags::enum_type f2) noexcept = delete; \
constexpr inline void operator+(Flags::enum_type f1, QFlags<Flags::enum_type> f2) noexcept = delete; \
constexpr inline void operator+(int f1, QFlags<Flags::enum_type> f2) noexcept = delete; \