Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QSet<myEnumClass> --> no matching function for call to 'qHash(myEnumClass&)'



  • Hi and thanks for reading,
    I have an enum class Operator::OperatorType that I want o use a QSet of. It's defined in the QObject-derived class Operator:

    #include "expression.h"
    class Operator: public Expression
    {
        Q_OBJECT
    public:
    
        enum class OperatorType {
            UNDEFINED,
            ADD, // add first to second
            SUB, // substract second from first
            MUL, // multiply first by second
            DIV, // divide first by second
            SQT, // secondth root of first
            SQU  // first to the power of second
        };
        Q_ENUM(OperatorType)
        //QHASH_FOR_CLASS_ENUM(OperatorType) // Ok
        Operator(OperatorType type, QSharedPointer<Expression> first, QSharedPointer<Expression> second);
    

    At another point in my code I try to build a QSet of this type:

        QSet<Operator::OperatorType> testTypes;
        Operator::OperatorType t = Operator::OperatorType::ADD;
        testTypes.insert(t);     // <--- Error points to this line
    

    This results in the following build error:

    C:\Qt\5.11.2\mingw53_32\include\QtCore\qhashfunctions.h:112: error: no matching function for call to 'qHash(const Operator::OperatorType&)'
         Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(t))) // error points to (t)
    

    As you can see (commented out in the above code), I have tried this idea:

    #define QHASH_FRIEND_FOR_CLASS_ENUM(T) \
    friend uint qHash(const T&, uint seed);
    
    #define QHASH_FOR_CLASS_ENUM(T) \
    inline uint qHash(const T &t, uint seed) { \
        return ::qHash(static_cast<typename std::underlying_type<T>::type>(t), seed); \
    } 
    

    Unfortuntely the problem remained. Am I doing something stupid? I'd not have thought it to be problematic, having a set of enums….

    What else would be recommendable? I'd rather not wrap the enum in a QVariant or attach it to a dummy object...


  • Moderators

    @SeDi
    move the definition of qHash() outside the class.

    As the error says, it is looking for qHash(const Operator::OperatorType&), not Operator::qHash(const OperatorType&)


  • Moderators

    To add to @raven-worx, here is the documentation on how to implement custom qHash.



  • Aaaaah…. Yess… I didn't see that. Works perfectly now. Thank you so much, raven-worx and sierdzio!



  • Are you sure you want to use QSet? QFlags seems more appropriate for enum values



  • Thank you for the idea @VRonin - I have looked into QFlags and I'd actually prefer that. But, at some point, I have to iterate over the enums stored inside - I haven't yet found a way to iterate over QFlags.

    What I actually try to achieve with the iteration (and it does work with QSet now) is to randomly select one of those enums.

    I don't want to rely on the QSet as being un-sorted in a random manner, so I pick a number by random and iterate to it:

        int number = qrand() % types.count();
        QSetIterator<Operator::OperatorType> iter(types);
        int i = 0;
        while (iter.hasNext()) {
            if (i == number) {
                return(iter.peekNext());
            }
            i++;
            iter.next();
        }
        return Operator::OperatorType::UNDEFINED;
    

    With QFlags I'd probably have to resort to some even uglier bitwise operators, I'd presume (without having given it too much thought).



  • enum OperatorType {
            UNDEFINED = 0,
            ADD = 0x1, // add first to second
            SUB = 0x2, // substract second from first
            MUL= 0x4, // multiply first by second
            DIV= 0x8, // divide first by second
            SQT= 0x10, // secondth root of first
            SQU =0x20   // first to the power of second
        };
    Q_FLAG(OperatorType)
    

    But, at some point, I have to iterate over the enums stored inside

    const auto flagMeta = QMetaEnum::fromType<Operator::OperatorType>();
        for(int i=0;i<flagMeta.keyCount();++i){
            qDebug() << flagMeta.key(i) << ": " << flagMeta.value(i);
        }
    

    even uglier bitwise operators

    const auto flagMeta = QMetaEnum::fromType<Operator::OperatorType>();
    const auto randomOperator = static_cast<Operator::OperatorType>(1 << (qrand() % (flagMeta.keyCount()-1)));
    

Log in to reply