Search code examples
c++vectornamespacesreference-wrapper

std::find fails on a std::vector<std::reference_wrapper<T>> with a "no match for ‘operator==’" error when T is in a namespace


I'm currently work on a large code project and wanted to take the opportunity to learn about and use namespaces. All of the classes I've defined reside within a single namespace, Test.

One of my classes, referred to here as Thing, has a unique ID. I need to be able to hold an std::vector of references to some Things, and so I'm using std::reference_wrappers. At points in the program, I need to remove certain std::reference_wrappers from the vector, so I use std::find:

#include <algorithm>
#include <functional>
#include <vector>

namespace Test {

class Thing {
private:
    const int id;

public:
    Thing(int id);
    const int ID() const;
};

}

Test::Thing::Thing(int id) : id(id) { }

const int Test::Thing::ID() const {
    return id;
}

inline bool operator==(const Test::Thing& lhs, const Test::Thing& rhs) {
    return lhs.ID() == rhs.ID();
}
inline bool operator!=(const Test::Thing& lhs, const Test::Thing& rhs) {
    return !(lhs == rhs);
}

int main() {
    Test::Thing t1(5);
    Test::Thing t2(7);

    auto ref1 = std::ref(t1);
    auto ref2 = std::ref(t2);

    std::vector<std::reference_wrapper<Test::Thing>> v;
    v.push_back(ref1);
    v.push_back(ref2);

    auto pos = std::find(v.begin(), v.end(), ref2);
}

When I attempt to compile this, I get an error:

error: no match for ‘operator==’ (operand types are ‘std::reference_wrapper<Test::Thing>’ and ‘const std::reference_wrapper<Test::Thing>’)

However, if I remove the namespace, the code compiles correctly:

#include <functional>
#include <vector>
#include <algorithm>

class Thing {
private:
    const int id;

public:
    Thing(int id);
    const int ID() const;
};

Thing::Thing(int id) : id(id) { }

const int Thing::ID() const {
    return id;
}

inline bool operator==(const Thing& lhs, const Thing& rhs) {
    return lhs.ID() == rhs.ID();
}
inline bool operator!=(const Thing& lhs, const Thing& rhs) {
    return !(lhs == rhs);
}

int main() {
    Thing t1(5);
    Thing t2(7);

    auto ref1 = std::ref(t1);
    auto ref2 = std::ref(t2);

    std::vector<std::reference_wrapper<Thing>> v;
    v.push_back(ref1);
    v.push_back(ref2);

    auto pos = std::find(v.begin(), v.end(), ref2);
}

Solution

  • The correct solution is to move the operators into the namespace where Argument-dependent lookup (ADL) can find them:

    namespace Test {
    
    class Thing {
    private:
        const int id;
    
    public:
        Thing(int id);
        const int ID() const;
    };
    
    
    inline bool operator==(const Thing& lhs, const Thing& rhs) {
        return lhs.ID() == rhs.ID();
    }
    inline bool operator!=(const Thing& lhs, const Thing& rhs) {
        return !(lhs == rhs);
    }
    
    }
    
    Test::Thing::Thing(int id) : id(id) { }
    
    const int Test::Thing::ID() const {
        return id;
    }
    

    [Live example]

    The same is already done by the standard library with operators such as << for stream insertion.