Search code examples
c++clangqt5

C++ structs equal operator: invalid operands to binary expression


compiler

clang version 12.0.0  
Target: x86_64-pc-windows-msvc  
struct FrameStyle
{
    QColor color; // has both == and != operators
    Qt::PenStyle penStyle; // enum
    Distance padding; // has == operator

    bool operator==(const FrameStyle& other)
    {
        return other.color == color
                && other.penStyle == penStyle
                && other.padding == padding;
    }
};

FrameStyle style;
FrameStyle m_prevStyle;


const bool styleChanged = !(style == m_prevStyle); // ok
const bool styleChanged = style != m_prevStyle; // Invalid operands to binary expression ('FrameStyle' and 'FrameStyle')
error: invalid operands to binary expression ('gns::FrameStyle' and 'gns::FrameStyle')
    const bool styleChanged = style != m_prevStyle;
                              ~~~~~ ^  ~~~~~~~~~~~
E:\5.15.8\msvc\include\QtCore/qchar.h(62,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'char' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(char lhs, QLatin1Char rhs) noexcept { return lhs != rhs.toLatin1(); }
                             ^
E:\5.15.8\msvc\include\QtCore/qchar.h(69,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QLatin1Char' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(QLatin1Char lhs, char rhs) noexcept { return lhs.toLatin1() != rhs; }
                             ^
E:\5.15.8\msvc\include\QtCore/qchar.h(640,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QChar' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(QChar c1, QChar c2) noexcept { return !operator==(c1, c2); }
                             ^
E:\5.15.8\msvc\include\QtCore/qchar.h(651,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QChar' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(QChar lhs, std::nullptr_t) noexcept { return !operator==(lhs, nullptr); }
                             ^
E:\5.15.8\msvc\include\QtCore/qchar.h(656,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'std::nullptr_t' (aka 'nullptr_t') for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(std::nullptr_t, QChar rhs) noexcept { return !operator==(nullptr, rhs); }
                             ^
E:\5.15.8\msvc\include\QtCore/qbytearray.h(690,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QByteArray' for 1st argument
inline bool operator!=(const QByteArray &a1, const QByteArray &a2) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qbytearray.h(692,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QByteArray' for 1st argument
inline bool operator!=(const QByteArray &a1, const char *a2) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qbytearray.h(694,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const char *' for 1st argument
inline bool operator!=(const char *a1, const QByteArray &a2) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qbytearray.h(811,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QByteArray::FromBase64Result' for 1st argument
inline bool operator!=(const QByteArray::FromBase64Result &lhs, const QByteArray::FromBase64Result &rhs) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1379,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QString::Null' for 1st argument
inline bool operator!=(QString::Null, QString::Null) { return false; }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1381,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QString::Null' for 1st argument
inline bool operator!=(QString::Null, const QString &s) { return !s.isNull(); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1383,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QString' for 1st argument
inline bool operator!=(const QString &s, QString::Null) { return !s.isNull(); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1388,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QLatin1String' for 1st argument
inline bool operator!=(QLatin1String s1, QLatin1String s2) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1432,32): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const char *' for 1st argument
inline QT_ASCII_CAST_WARN bool operator!=(const char *s1, const QString &s2)
                               ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1445,32): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const char *' for 1st argument
inline QT_ASCII_CAST_WARN bool operator!=(const char *s1, QLatin1String s2)
                               ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1822,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QStringRef' for 1st argument
inline bool operator!=(const QStringRef &s1, const QStringRef &s2) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1834,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QString' for 1st argument
inline bool operator!=(const QString &lhs, const QStringRef &rhs) noexcept { return lhs.compare(rhs) != 0; }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1841,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QStringRef' for 1st argument
inline bool operator!=(const QStringRef &lhs, const QString &rhs) noexcept { return rhs != lhs; }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1870,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QLatin1String' for 1st argument
inline bool operator!=(QLatin1String lhs, const QStringRef &rhs) noexcept { return rhs.compare(lhs) != 0; }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1877,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QStringRef' for 1st argument
inline bool operator!=(const QStringRef &lhs, QLatin1String rhs) noexcept { return rhs != lhs; }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1891,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QChar' for 1st argument
inline bool operator!=(QChar lhs, const QString &rhs) noexcept { return !(lhs == rhs); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1896,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QString' for 1st argument
inline bool operator!=(const QString &lhs, QChar rhs) noexcept { return !(rhs == lhs); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1910,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QChar' for 1st argument
inline bool operator!=(QChar lhs, const QStringRef &rhs) noexcept { return !(lhs == rhs); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1915,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QStringRef' for 1st argument
inline bool operator!=(const QStringRef &lhs, QChar rhs) noexcept { return !(rhs == lhs); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1929,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QChar' for 1st argument
inline bool operator!=(QChar lhs, QLatin1String rhs) noexcept { return !(lhs == rhs); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1934,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QLatin1String' for 1st argument
inline bool operator!=(QLatin1String lhs, QChar rhs) noexcept { return !(rhs == lhs); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1942,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QStringView' for 1st argument
inline bool operator!=(QStringView lhs, QStringView rhs) noexcept { return !(lhs == rhs); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1950,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QStringView' for 1st argument
inline bool operator!=(QStringView lhs, QChar rhs) noexcept { return lhs != QStringView(&rhs, 1); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1957,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QChar' for 1st argument
inline bool operator!=(QChar lhs, QStringView rhs) noexcept { return QStringView(&lhs, 1) != rhs; }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1965,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QStringView' for 1st argument
inline bool operator!=(QStringView lhs, QLatin1String rhs) noexcept { return !(lhs == rhs); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1972,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QLatin1String' for 1st argument
inline bool operator!=(QLatin1String lhs, QStringView rhs) noexcept { return !(lhs == rhs); }
            ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1981,32): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QStringRef' for 1st argument
inline QT_ASCII_CAST_WARN bool operator!=(const QStringRef &lhs, const QByteArray &rhs) { return lhs.compare(rhs) != 0; }
                               ^
E:\5.15.8\msvc\include\QtCore/qstring.h(1988,32): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QByteArray' for 1st argument
inline QT_ASCII_CAST_WARN bool operator!=(const QByteArray &lhs, const QStringRef &rhs) { return rhs.compare(lhs) != 0; }
                               ^
E:\5.15.8\msvc\include\QtCore/qstring.h(2010,32): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const char *' for 1st argument
inline QT_ASCII_CAST_WARN bool operator!=(const char *s1, const QStringRef &s2)
                               ^
E:\5.15.8\msvc\include\QtCore/qpoint.h(168,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QPoint' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QPoint &p1, const QPoint &p2)
                             ^
E:\5.15.8\msvc\include\QtCore/qpoint.h(363,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QPointF' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QPointF &p1, const QPointF &p2)
                             ^
E:\5.15.8\msvc\include\QtCore/qvariant.h(616,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QVariant' for 1st argument
inline bool operator!=(const QVariant &v1, const QVariantComparisonHelper &v2)
            ^
E:\5.15.8\msvc\include\QtCore/qmetaobject.h(202,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QMetaMethod' for 1st argument
inline bool operator!=(const QMetaMethod &m1, const QMetaMethod &m2)
            ^
E:\5.15.8\msvc\include\QtCore/qmargins.h(144,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QMargins' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QMargins &m1, const QMargins &m2) noexcept
                             ^
E:\5.15.8\msvc\include\QtCore/qmargins.h(380,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QMarginsF' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
                             ^
E:\5.15.8\msvc\include\QtCore/qsize.h(178,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QSize' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QSize &s1, const QSize &s2) noexcept
                             ^
E:\5.15.8\msvc\include\QtCore/qsize.h(353,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QSizeF' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QSizeF &s1, const QSizeF &s2) noexcept
                             ^
E:\5.15.8\msvc\include\QtCore/qrect.h(459,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QRect' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QRect &r1, const QRect &r2) noexcept
                             ^
E:\5.15.8\msvc\include\QtCore/qrect.h(866,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QRectF' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QRectF &r1, const QRectF &r2) noexcept
                             ^
E:\5.15.8\msvc\include\QtGui/qvector2d.h(216,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QVector2D' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QVector2D &v1, const QVector2D &v2)
                             ^
E:\5.15.8\msvc\include\QtGui/qevent.h(877,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'QPointingDeviceUniqueId' for 1st argument
inline bool operator!=(QPointingDeviceUniqueId lhs, QPointingDeviceUniqueId rhs) noexcept
            ^
C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared/guiddef.h(197,15): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const GUID' (aka 'const _GUID') for 1st argument
__inline bool operator!=(REFGUID guidOne, REFGUID guidOther)
              ^
E:\5.15.8\msvc\include\QtGui/qvector3d.h(241,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QVector3D' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QVector3D &v1, const QVector3D &v2)
                             ^
E:\5.15.8\msvc\include\QtGui/qvector4d.h(241,30): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QVector4D' for 1st argument
Q_DECL_CONSTEXPR inline bool operator!=(const QVector4D &v1, const QVector4D &v2)
                             ^
E:\5.15.8\msvc\include\QtGui/qquaternion.h(289,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QQuaternion' for 1st argument
inline bool operator!=(const QQuaternion &q1, const QQuaternion &q2)
            ^
E:\5.15.8\msvc\include\QtGui/qsurfaceformat.h(173,19): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QSurfaceFormat' for 1st argument
Q_GUI_EXPORT bool operator!=(const QSurfaceFormat&, const QSurfaceFormat&);
                  ^
E:\5.15.8\msvc\include\QtGui/qcursor.h(130,13): note: candidate function not viable: no known conversion from 'gns::FrameStyle' to 'const QCursor' for 1st argument
inline bool operator!=(const QCursor &lhs, const QCursor &rhs) noexcept { return !(lhs == rhs); }
            ^
E:\5.15.8\msvc\include\QtCore/qpair.h(118,41): note: candidate template ignored: could not match 'QPair<type-parameter-0-0, type-parameter-0-1>' against 'gns::FrameStyle'
Q_DECL_CONSTEXPR Q_INLINE_TEMPLATE bool operator!=(const QPair<T1, T2> &p1, const QPair<T1, T2> &p2)
                                        ^
E:\5.15.8\msvc\include\QtCore/qscopedpointer.h(190,13): note: candidate template ignored: could not match 'QScopedPointer<type-parameter-0-0, type-parameter-0-1>' against 'gns::FrameStyle'
inline bool operator!=(const QScopedPointer<T, Cleanup> &lhs, const QScopedPointer<T, Cleanup> &rhs) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qscopedpointer.h(208,13): note: candidate template ignored: could not match 'QScopedPointer<type-parameter-0-0, type-parameter-0-1>' against 'gns::FrameStyle'
inline bool operator!=(const QScopedPointer<T, Cleanup> &lhs, std::nullptr_t) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qscopedpointer.h(214,13): note: candidate template ignored: could not match 'QScopedPointer<type-parameter-0-0, type-parameter-0-1>' against 'gns::FrameStyle'
inline bool operator!=(std::nullptr_t, const QScopedPointer<T, Cleanup> &rhs) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qvarlengtharray.h(573,6): note: candidate template ignored: could not match 'QVarLengthArray<type-parameter-0-0, Prealloc>' against 'gns::FrameStyle'
bool operator!=(const QVarLengthArray<T, Prealloc1> &l, const QVarLengthArray<T, Prealloc2> &r)
     ^
E:\5.15.8\msvc\include\QtCore/qsharedpointer_impl.h(752,6): note: candidate template ignored: could not match 'QSharedPointer<type-parameter-0-0>' against 'gns::FrameStyle'
bool operator!=(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2) noexcept
     ^
E:\5.15.8\msvc\include\QtCore/qsharedpointer_impl.h(768,6): note: candidate template ignored: could not match 'QSharedPointer<type-parameter-0-0>' against 'gns::FrameStyle'
bool operator!=(const QSharedPointer<T> &ptr1, const X *ptr2) noexcept
     ^
E:\5.15.8\msvc\include\QtCore/qsharedpointer_impl.h(773,6): note: candidate template ignored: could not match 'const T *' against 'gns::FrameStyle'
bool operator!=(const T *ptr1, const QSharedPointer<X> &ptr2) noexcept
     ^
E:\5.15.8\msvc\include\QtCore/qsharedpointer_impl.h(784,6): note: candidate template ignored: could not match 'QSharedPointer<type-parameter-0-0>' against 'gns::FrameStyle'
bool operator!=(const QSharedPointer<T> &ptr1, const QWeakPointer<X> &ptr2) noexcept
     ^
E:\5.15.8\msvc\include\QtCore/qsharedpointer_impl.h(796,13): note: candidate template ignored: could not match 'QSharedPointer<type-parameter-0-0>' against 'gns::FrameStyle'
inline bool operator!=(const QSharedPointer<T> &lhs, std::nullptr_t) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qsharedpointer_impl.h(808,13): note: candidate template ignored: could not match 'QSharedPointer<type-parameter-0-0>' against 'gns::FrameStyle'
inline bool operator!=(std::nullptr_t, const QSharedPointer<T> &rhs) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qsharedpointer_impl.h(820,13): note: candidate template ignored: could not match 'QWeakPointer<type-parameter-0-0>' against 'gns::FrameStyle'
inline bool operator!=(const QWeakPointer<T> &lhs, std::nullptr_t) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qsharedpointer_impl.h(832,13): note: candidate template ignored: could not match 'QWeakPointer<type-parameter-0-0>' against 'gns::FrameStyle'
inline bool operator!=(std::nullptr_t, const QWeakPointer<T> &rhs) noexcept
            ^
E:\5.15.8\msvc\include\QtCore/qpointer.h(114,13): note: candidate template ignored: could not match 'const T *' against 'gns::FrameStyle'
inline bool operator!=(const T *o, const QPointer<T> &p)
            ^
E:\5.15.8\msvc\include\QtCore/qpointer.h(118,13): note: candidate template ignored: could not match 'QPointer<type-parameter-0-0>' against 'gns::FrameStyle'
inline bool operator!= (const QPointer<T> &p, const T *o)
            ^
E:\5.15.8\msvc\include\QtCore/qpointer.h(122,13): note: candidate template ignored: could not match 'T *' against 'gns::FrameStyle'
inline bool operator!=(T *o, const QPointer<T> &p)
            ^
E:\5.15.8\msvc\include\QtCore/qpointer.h(126,13): note: candidate template ignored: could not match 'QPointer<type-parameter-0-0>' against 'gns::FrameStyle'
inline bool operator!= (const QPointer<T> &p, T *o)
            ^
E:\5.15.8\msvc\include\QtCore/qpointer.h(130,13): note: candidate template ignored: could not match 'QPointer<type-parameter-0-0>' against 'gns::FrameStyle'
inline bool operator!= (const QPointer<T> &p1, const QPointer<T> &p2)
            ^
1 error generated.

Why do I ever have to overload the equal operator in this case?
Why don't I get automatic not equal operator?


Solution

  • This is how C++ used to be since its inception... until C++20, in which default comparisons were introduced, https://en.cppreference.com/w/cpp/language/default_comparisons.

        bool operator==(const FrameStyle& other) const = default;  // c++20
    

    (this will provide also a consistent operator!= as I understand)

    It has an interesting backstory involving Dennis Ritchie (from C), Stroustrup, and Stepanov. Some people were worried that element-by-element equality would mean the incorrect thing for structures where data is indirected (e.g., the pointer member in a dynamic array). The counterargument was that this was inconsistent with operator=, which is generally provided by default, and it is very useful, even if naively it means the incorrect thing.

    I agree with the opinion that operator= and operator== (and operator!=, and perhaps many more) should be provided default, and it is the developer's responsibility to change their meaning when necessary.

    Having said that, in your case, I see two problems:

    • Make the method const, bool operator==(const FrameStyle& other) const
    • Make it a friend function. This will save some headaches.