Search code examples
c++templatesoverloadingsfml

Error when overloading operator< in sf::Vector


I want to use std::map with sf::Vector2i class, but for some reason I need to overload < operator. Since the map will represent a 3x3 grid, I came up with this to sort the vectors:

inline bool operator <(sf::Vector2i l, sf::Vector2i r)
{
    if (l.x * 3 + l.y < r.x * 3 + r.y) return 1;
    return 0;
}

But I still get error C2678

no operator found which takes a left-hand operand of type 'const sf::Vector2i'

I did the same thing with my custom vector type and it worked, so what is causing this error?

Edit:

This overload is in a separate .cpp file. I just wrapped this overload in a namespace sf{} and it worked. Why doesn't it work by just specifying sf::Vector2i in the parameters?


Solution

  • Let's start with:

    I want to use std::map with sf::Vector2i class, but for some reason I need to overload < operator.

    A std::map orders its contents by their keys, which requires knowing how to order the keys (presumably sf::Vector2i). By default, the definition of the order of the keys is given by operator <. That means that the map calls operator<(const sf::Vector21&, const sf::Vector21&) to determine if one key is less than another. Hence, this operator must be defined somewhere.

    If you are not interested in this order, you could try an unordered_map instead.

    I just wrapped this overload in a namespace sf{} and it worked. Why doesn't it work by just specifying sf::Vector2i in the parameters?

    This is probably a result of the lookup rules for dependent names. Remember that call to operator<? It would be made somewhere deep inside templates, and the types of its arguments are dependent on template parameters. The relevant consequence for this is that the compiler will look for declarations visible from the template definition (deep in header files), plus whatever is found by argument-dependent lookup (ADL). Your overload is not visible from the template definition (nor should it), so it would need to be found by ADL. However, the arguments are both in the namespace sf, so ADL looks only in namespace sf. Hence the compiler can find an overload in the sf namespace, but not one elsewhere.

    This is more commonly encountered when the arguments are in the std namespace, but should apply here as well. (Some of this is newish to me, so I might have messed up some details.) The main takeaway is that trying to override operators acting on only classes in another namespace often fails.

    The good news is that you are not stuck. A std::map takes more than two template parameters. The third one lets you specify how keys are to be compared. Read up on the subject and see how far you can get.