views:

330

answers:

4

I have a vector of Student objects which I want to sort using #include <algorithm> and sort(list.begin(), list.end());

In order to do this, I understand that I need to overload the "<" operator but after trying (and failing) with several methods suggested online, I am running out of ideas.

Here is my latest attempt:

In Student.h...

...
using namespace std;
class Student
{
    friend bool operator <(const Student& first, const Student& second);
    public:
    ...
    private:
    ...
};

And in Student.cpp...

...
#include "Student.h"
using namespace std;
...
bool operator <(const Student& first, const Student& second)
{
    return first.Name() < second.Name();
}

where "Name()" is a constant function which returns a string.

The program compiles and runs, but my operator function is never called during the sort and when I tried comparing two Student objects like s1 < s2 I got an "error: overloaded operator not found"

How can I correctly overload this operator so that my sort will work as I intend?

+1  A: 

Well, you could do it as an internal operator:

class Student
{
    public:
    bool operator <(const Student& second) const;
    ...
    private:
    ...
};

With an implementation comparing 'this' to second. The question in my mind is: Does the 'Name' method come in a const flavor? Because if it doesn't, then you can't write a const method that uses it.

Michael Kohne
Seems likely - both the "first" and "second" parameters in the friend version were const.
Steve314
+5  A: 

I wouldn't use a friend here, and I'm not sure it works at all. What I would use is...

class Student
{
  public:
    bool operator< (const Student& second) const;
};

bool Student::operator< (const Student& second) const
{
  return (Name() < second.Name());
}

Note the trailing const, indicating that within operator<, *this is constant.

Steve314
I think I've finally decided on this as the answer I will pick, because it is the one that most directly led me to identify the greater problem that I had with my pointers.
Paul Williams
This approach is often avoided because if types are convertible to Student, the code will convert the right-side type but not the left-side type. A friend function would behave consistently for both sides.
Shmoopty
+9  A: 

You didn't say which compiler you were using, but I suspect you're using a fairly recent one that implements the "friend is not a declaration" rule. The friend statement inside the class doesn't serve as a function declaration; other modules that include Student.h don't see any declaration of the function. It's visible only inside the Student.cpp file. (Older compilers don't have this rule, and treat a friend declaration as a function declaration.)

The function doesn't need to be a friend, since it doesn't use any private members of the Student class (I'm assuming Name() is public). Move the function declaration outside the class, and replace "friend" with "extern", and it should work.

It is possible to make the operator a member function, as some posters above suggested, but it isn't necessary. Making comparison operators member functions is generally frowned upon, because it means that the two arguments aren't treated symmetrically (one is the invisible "this" argument, the other is a normal function argument), which can lead to surprising results in some cases (for example, different type conversions may be applied to the arguments).

Ross Smith
Is it necessary to mark the function declaration extern? I've only every done that with variables declared in header files.
Anthony Cramp
It was hard to decide on a single answer for this one. My original attempts were all defined within the class and I only tried the "friend" version out of desperation after reading someone's claim that "sort" wouldn't be effected unless you did so.It turned out that I actually had a pointer-related problem as well.
Paul Williams
True, it isn't really necessary; functions outside classes have extern linkage by default. I've got in the habit of putting it in to indicate to the reader that the function is defined in another source file (as opposed to being a forward declaration of a function defined elsewhere in the same file).
Ross Smith
@Ross Smith: good point, I like it.
Anthony Cramp
@Ross - I understand the frowning, but I personally frown at the people who wrote the standard. The logical symmetrical approach is to have a static member function, but apparantly that's not allowed. Anyway, my habits were formed because of problems with the friend approach - those problems may be gone by now, but my current habits don't cause me any problems. IMO, "If it ain't broke don't fix it" applies to habits too.
Steve314
+2  A: 

By the way, with a function this simple I would personally just do this, unless the style guide bans the definition of functions inside class definitions:

class Student
{
    friend bool operator <(const Student& first, const Student& second)
    {
        return first.Name() < second.Name();
    }
    ...
};

This form of the "friend" declaration lets you define a non-member operator< inside the body of the class. It's still a friend, so Name() can be private if desired.

Steve Jessop