Evgeni Sergeev
Evgeni Sergeev

Reputation: 23611

Why "no match for 'operator<'" when I declared it?

It works for the struct xy that I declared. Why doesn't the same pattern work for complex<int>?

#include <complex>
#include <set>
using namespace std;

struct xy {
    int x, y;
};

bool operator< (const xy &a, const xy &b) {
    return a.x < b.x;
}

bool operator< (const complex<int> &a, const complex<int> &b) {
    return a.real() < b.real();
}

int main() {
    xy q;
    set<xy> s;
    s.insert(q);

    complex<int> p;
    set< complex<int> > t;   //If I comment out these two lines,
    t.insert(p);             //it compiles fine.

    return 0;
}

The error message:

In file included from c:\m\lib\gcc\mingw32\4.8.1\include\c++\string:48:0,
                 from c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\locale_classes.h:40,
                 from c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\ios_base.h:41,
                 from c:\m\lib\gcc\mingw32\4.8.1\include\c++\ios:42,
                 from c:\m\lib\gcc\mingw32\4.8.1\include\c++\istream:38,
                 from c:\m\lib\gcc\mingw32\4.8.1\include\c++\sstream:38,
                 from c:\m\lib\gcc\mingw32\4.8.1\include\c++\complex:45,
                 from test.cpp:1:
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_function.h: In instantiation of 'bool less<>::operator()(const _Tp&, const _Tp&) const':
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_tree.h:1321:11:   required from 'pair<> _Rb_tree<>::_M_get_insert_unique_pos(const key_type&)'
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_tree.h:1374:47:   required from 'pair<> _Rb_tree<>::_M_insert_unique(_Arg&&)'
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_set.h:463:29:   required from 'pair<> __cxx1998::set<>::insert(const value_type&)'
c:\m\lib\gcc\mingw32\4.8.1\include\c++\debug\set.h:220:59:   required from 'pair<> __debug::set<>::insert(const value_type&)'
test.cpp:28:19:   required from here
c:\m\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_function.h:235:20: 
error: no match for 'operator<' (operand types are 'const std::complex<int>' and 'const std::complex<int>')
       { return __x < __y; }

My best guess is that this has something to do with complex<T> being a class, not a struct. By I can't see the logic of why that should make a difference. Or is it some template horribleness?

What I see happening is that the STL at some point tries (roughly speaking) to do a < b, where a and b are complex<int> instances. So it's looking for bool operator< (const complex<int> &a, const complex<int> &b). Well, there is exactly that declared just above main(). Why is it being unreasonable? I thought maybe it didn't like them being references. But removing the ampersands made no difference to its complaint.

Upvotes: 4

Views: 1716

Answers (3)

iammilind
iammilind

Reputation: 70048

As of today, I am facing this issue with my Ubuntu's g++. Suppose I have:

namespace X { namespace Y { class C { ... }; } }

Now operator== is recognized if it's defined in a global namespace, such as:

bool operator== (const X::Y::C& lhs, const X::Y::C& rhs) { return lhs == rhs; }

However, somehow compiler doesn't recognize operator<, if defined in the same way.

Now following way works well:

namespace X { namespace Y { 
     bool operator< (const C& lhs, const C& rhs) { return lhs < rhs; } } }

However, whether you should do it the same with expanding standard namespace std is a controversial topic. :-)

Upvotes: 0

Bo Persson
Bo Persson

Reputation: 92341

It works for the struct xy that I declared. Why doesn't the same pattern work for complex<int>?

The reason is that when you use types from namespace std only, like std::set and std::complex, the compiler has no reason to look for operators in any other namespaces.

With struct xy this is different, as the operator is declared together with the type.


And also, the current standard says:

The effect of instantiating the template complex for any type other than float, double, or long double is unspecified.

So using complex<int> might, or might not, work depending on which compiler you use. "Unspecified" is not as bad as "undefined", but of course not very reliable or portable.

Upvotes: 3

Dan Mašek
Dan Mašek

Reputation: 19061

One option is to write a custom comparison functor, and instantiate the set with this.

#include <complex>
#include <set>    

bool operator< (const std::complex<int> &a, const std::complex<int> &b) {
    return a.real() < b.real();
}

struct my_less {
    bool operator() (const std::complex<int>& lhs, const std::complex<int>& rhs) const{
        return lhs < rhs;
    }
};

int main() {
    std::complex<int> p;
    std::set< std::complex<int>, my_less > t;
    t.insert(p);

    return 0;
}

Sample on Coliru

Upvotes: 3

Related Questions