Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-87973

Overload resolution noise from global operators and functions

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P2: Important
    • 6.0.0
    • 6.0.0 Beta2
    • Other
    • None
    • 1ba4f7369 (dev)

    Description

      Trying to build the following example generates a substantial amount of output from the compiler trying the various global comparison operators declared for Qt types:

      #include <QtCore>
      #include <QtGui>
      #include <QtWidgets>
      #include <QtNetwork>
      #include <QtXml>
      #include <QtSql>
      #include <QtQuick>
      
      
      struct A {};
      struct B {};
      
      int main ()
      {
          A a1, a2;
          B b1, b2;
      
          auto x = a1 == b2;
          auto y = a2 != b1;
      
          return 0;
      }
      

      The reason being that the operators are declared as global functions, so are participating in the compilers argument-dependent lookup (ADL):

      [build] main.cpp:18:17: error: invalid operands to binary expression ('A' and 'B')
      [build]     auto x = a1 == b2;
      [build]              ~~ ^  ~~
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtCore/../../../../qtbase/src/corelib/serialization/qcborcommon.h:94:13: note: candidate function not viable: no known conversion from 'A' to 'QCborTag' for 1st argument
      [build] inline bool operator==(QCborTag t, QCborKnownTags kt)   { return quint64(t) == quint64(kt); }
      [build]             ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtCore/../../../../qtbase/src/corelib/serialization/qcborcommon.h:95:13: note: candidate function not viable: no known conversion from 'A' to 'QCborKnownTags' for 1st argument
      [build] inline bool operator==(QCborKnownTags kt, QCborTag t)   { return quint64(t) == quint64(kt); }
      [build]             ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtCore/../../../../qtbase/src/corelib/tools/qversionnumber.h:410:23: note: candidate function not viable: no known conversion from 'A' to 'QTypeRevision' for 1st argument
      [build] inline constexpr bool operator==(QTypeRevision lhs, QTypeRevision rhs)
      [build]                       ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtGui/../../../../qtbase/src/gui/accessible/qaccessible.h:440:19: note: candidate function not viable: no known conversion from 'A' to 'const QAccessible::State' for 1st argument
      [build] Q_GUI_EXPORT bool operator==(const QAccessible::State &first, const QAccessible::State &second);
      [build]                   ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtNetwork/../../../../qtbase/src/network/kernel/qhostaddress.h:158:13: note: candidate function not viable: no known conversion from 'A' to 'QHostAddress::SpecialAddress' for 1st argument
      [build] inline bool operator ==(QHostAddress::SpecialAddress address1, const QHostAddress &address2)
      [build]             ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtNetwork/../../../../qtbase/src/network/access/qhstspolicy.h:92:23: note: candidate function not viable: no known conversion from 'A' to 'const QHstsPolicy' for 1st argument
      [build] Q_NETWORK_EXPORT bool operator==(const QHstsPolicy &lhs, const QHstsPolicy &rhs);
      [build]                       ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtNetwork/../../../../qtbase/src/network/access/qhttp2configuration.h:91:23: note: candidate function not viable: no known conversion from 'A' to 'const QHttp2Configuration' for 1st argument
      [build] Q_NETWORK_EXPORT bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs);
      [build]                       ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtNetwork/../../../../qtbase/src/network/ssl/qssldiffiehellmanparameters.h:66:23: note: candidate function not viable: no known conversion from 'A' to 'const QSslDiffieHellmanParameters' for 1st argument
      [build] Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) noexcept;
      [build]                       ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtNetwork/../../../../qtbase/src/network/ssl/qsslellipticcurve.h:90:23: note: candidate function not viable: no known conversion from 'A' to 'QSslEllipticCurve' for 1st argument
      [build] constexpr inline bool operator==(QSslEllipticCurve lhs, QSslEllipticCurve rhs) noexcept
      [build]                       ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtOpenGL/../../../../qtbase/src/opengl/qopenglversionfunctions.h:105:23: note: candidate function not viable: no known conversion from 'A' to 'const QOpenGLVersionStatus' for 1st argument
      [build] constexpr inline bool operator==(const QOpenGLVersionStatus &lhs, const QOpenGLVersionStatus &rhs)
      [build]                       ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtOpenGL/../../../../qtbase/src/opengl/qopenglversionprofile.h:85:13: note: candidate function not viable: no known conversion from 'A' to 'const QOpenGLVersionProfile' for 1st argument
      [build] inline bool operator==(const QOpenGLVersionProfile &lhs, const QOpenGLVersionProfile &rhs)
      [build]             ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtQuick/../../../../qtdeclarative/src/quick/items/qquickrendertarget.h:93:21: note: candidate function not viable: no known conversion from 'A' to 'const QQuickRenderTarget' for 1st argument
      [build] Q_QUICK_EXPORT bool operator==(const QQuickRenderTarget &a, const QQuickRenderTarget &b) Q_DECL_NOTHROW;
      [build]                     ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtCore/../../../../qtbase/src/corelib/tools/qlist.h:860:35: note: candidate template ignored: could not match 'QList<type-parameter-0-0>' against 'A'
      [build] QTypeTraits::compare_eq_result<U> operator==(const QList<U> &l, const QList<U> &r)
      [build]                                   ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtCore/../../../../qtbase/src/corelib/tools/qscopedpointer.h:176:13: note: candidate template ignored: could not match 'QScopedPointer<type-parameter-0-0, type-parameter-0-1>' against 'A'
      [build] inline bool operator==(const QScopedPointer<T, Cleanup> &lhs, const QScopedPointer<T, Cleanup> &rhs) noexcept
      [build]             ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtCore/../../../../qtbase/src/corelib/tools/qscopedpointer.h:188:13: note: candidate template ignored: could not match 'QScopedPointer<type-parameter-0-0, type-parameter-0-1>' against 'A'
      [build] inline bool operator==(const QScopedPointer<T, Cleanup> &lhs, std::nullptr_t) noexcept
      [build]             ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtCore/../../../../qtbase/src/corelib/tools/qscopedpointer.h:194:13: note: candidate template ignored: could not match 'QScopedPointer<type-parameter-0-0, type-parameter-0-1>' against 'B'
      [build] inline bool operator==(std::nullptr_t, const QScopedPointer<T, Cleanup> &rhs) noexcept
      [build]             ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtCore/../../../../qtbase/src/corelib/tools/qvarlengtharray.h:630:35: note: candidate template ignored: could not match 'QVarLengthArray<type-parameter-0-0, Prealloc>' against 'A'
      [build] QTypeTraits::compare_eq_result<T> operator==(const QVarLengthArray<T, Prealloc1> &l, const QVarLengthArray<T, Prealloc2> &r)
      [build]                                   ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtCore/../../../../qtbase/src/corelib/tools/qmap.h:738:42: note: candidate template ignored: could not match 'QMap<type-parameter-0-0, type-parameter-0-1>' against 'A'
      [build] QTypeTraits::compare_eq_result<AKey, AT> operator==(const QMap<AKey, AT> &lhs, const QMap<AKey, AT> &rhs)
      [build]                                          ^
      [build] /Users/vohi/qt/dev/build/qtbase/include/QtCore/../../../../qtbase/src/corelib/tools/qmap.h:1437:42: note: candidate template ignored: could not match 'QMultiMap<type-parameter-0-0, type-parameter-0-1>' against 'A'
      [build] QTypeTraits::compare_eq_result<AKey, AT> operator==(const QMultiMap<AKey, AT> &lhs, const QMultiMap<AKey, AT> &rhs)
      

      Making those operators hidden friends by declaring them as friends AND implementing them directly and inline in the respective class declaration will hide these operators from ADL. This gets rid of the noise, and speed up compilation. Candidate operators and functions for which this should be done are those for which there exist many overloads, e.g.

      • equality: operator== and operator!=
      • comparison: operator<,>,<=,>=
      • arithmetic: operator+,- etc
      • swap
      • qHash() (→ subtask)
      • QDebug and QDataStream operator<< (→ subtask)

      In order to implement such operators as inline methods, esp for pimpl'ed classes, it might be necessary to add a private helper method (such as bool equals(const QType &other) const or int compare(const QType &other) const as a member to the public class. It is not necessary to export the operators, since they will be inline.

      The documentation often needs to be fixed as well; qdoc expects hidden friends to be documented as if they were members of the respective class (as that's where they live in clang's AST), e.g.

      /*!
        /fn bool QValueType::operator==(const QValueType &lhs, const QValueType &rhs)
      */
      

      Yes, this is not valid C++.

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            vhilshei Volker Hilsheimer
            vhilshei Volker Hilsheimer
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: