Search code examples
c++operator-overloadingoperatorsoverloadingglm-math

Failed to overload glm::vec2 '<' operator


I need to use std::map with glm::vec2 so I tried to implement '<' operator but it fails. ( std::map needs this operator )

Here is the code from my test example:

bool operator <(const glm::vec2& l, const glm::vec2& r)
{
    int lsize = l.x + l.y;
    int rsize = r.x + r.y;

    return lsize < rsize;
}


class A
{
public:
    A()
    {
        test[glm::vec2(5, 5)] = 4;
    }
private:
    std::map<glm::vec2, int> test;
};


int _tmain(int argc, _TCHAR* argv[])
{
    A a;
    return 0;
}

When I create my own Vector2 class and implement the operator same way up there, it compiles, but it fails with glm::vec2 ( I have 19 errors telling the operator '<' is not defined for glm::vec2 etc. )

error C2676: binary '<' : 'const glm::vec2' does not define this operator or a conversion to a type acceptable to the predefined operator   

Note that my operator overloading function compiles, the error comes from the use of std::map, seems like the '<' glm::vec2 operator is still considered as undefined.

Here is GLM sources if it can help you : https://github.com/g-truc/glm/tree/master/glm


Solution

  • It concerns me that you are even trying to do this in the first place, because I think this is probably not what you actually want to do. But if you actually want to do it, you need to put it in the right namespace.

    namespace glm {
    namespace detail {
    // But don't do this!  This is not a good idea...
    bool operator <(const glm::vec2& l, const glm::vec2& r)
    {
        int lsize = l.x + l.y;
        int rsize = r.x + r.y;
        return lsize < rsize;
    }
    }
    }
    

    But wait! This is wrong!

    • vec2 aren't ordered, so implementing < doesn't make any sense mathematically
    • it's not a good idea to override operators on library types
    • vec2 aren't ordered, so you shouldn't be putting them in a std::map

    A std::map keeps elements inside it ordered. So, what happens when you do this?

    int main(int argc, char* argv[])
    {
        std::map<glm::vec2, int> m;
        m[glm::vec2(1, 1)] = 10;
        std::cout << m[glm::vec2(0, 2)] << '\n';
        return 0;
    }
    

    Yes, this prints out 10 even though we never added (0, 2) to the map.

    You probably want to use a spatial index (quatdree, for example) or at the very least, use a lexicographic order. Barring that, if this is the behavior you actually want, you should be changing the comparator on your std::map via template arguments.

    struct vec2_cmp {
        bool operator()(const glm::vec2 &x, const glm::vec2 &y) { ... }
    }
    
    std::map<glm::vec2, int, vec2_cmp> m;