0

I have a typedef struct defined like so:

typedef struct myStruct {
  int id;
  double value;

  bool operator <(const myStruct &x, const myStruct &y) {
    return (x.id < y.id);
  }
} myStruct;

I need to use this struct as a key in a std::map, thus the overloaded operator. However, I get the following error message when trying to compile this:

overloaded 'operator<' must be a binary operator (has 3 parameters)

Okay, so I tried this instead:

bool operator <(const pointcloud_keyframe &x) {
  return (this->id < x.id);
}

However, that doesn't work either as I get this error message when trying to insert into the map:

invalid operands to binary expression ('const myStruct' and 'const myStruct')

Please help!

user1765354
  • 347
  • 1
  • 5
  • 21
  • 6
    You seem to be confusing member `operator<` with non-member `operator<`. – François Andrieux May 11 '17 at 18:47
  • 1
    Throw away your book, and pick a new one. Learn C++ in a **structured manner**. http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list – StoryTeller - Unslander Monica May 11 '17 at 18:48
  • 1
    Member `operator<` has the left-hand operand passed as `this` (implicit) so a member `operator<` should only have one declared argument (which will be the right-hand argument). Free operators (not class members) would have two. Your second attempt fails because the `operator<` member function is not declared `const` -- while it accepts a right-hand `const` argument, the left-hand side can't be `const` with a non-`const` operator. It should be declared `bool operator <(const pointcloud_keyframe &x) const { ...` – cdhowie May 11 '17 at 18:51
  • [This](http://stackoverflow.com/questions/4421706/operator-overloading) covers about everything to do with operator overloading. – NathanOliver May 11 '17 at 19:00
  • Why the second attempt is for `pointcloud_keyframe`? I thought you need to compare two `myStruct`. Btw, `struct` in C++ is typedef-ed by default, you don't need `typedef`. – facetus May 11 '17 at 19:01
  • 3
    Do not use `typedef struct` especially in C++, it is ugly and redunadant – Slava May 11 '17 at 19:03
  • There is no such thing as "a typedef struct". – Lightness Races in Orbit May 11 '17 at 19:23
  • I don't think it's really clean to implement `operator<` only because you need a sort predicate for `std::map`, especially if you compare only a single member of the struct. I would prefer to create a functor to be used with 3rd template parameter of `std::map` instead. – zett42 May 11 '17 at 19:48

2 Answers2

5
struct myStruct {
  int id;
  double value;

  friend bool operator <(const myStruct &x, const myStruct &y) {
    return (x.id < y.id);
  }
};

the key part is friend. I also removed the typedef; in C++ struct myStruct already defines a type named myStruct, no need to also typedef it.

There are other ways to make your code compile, but this is the easiest.

Without friend, your operator< is a member function, and member operator< takes one argument plus an implicit this.1

With friend, it becomes a "free function" that takes 2 arguments. I find this is the cleanest way to do it. It still has full permission to access private bits of your struct (which it may not need).

You could also move it outside of the struct itself

struct myStruct {
  int id;
  double value;

};
inline bool operator <(const myStruct &x, const myStruct &y) {
  return (x.id < y.id);
}

but < being a friend is relatively harmless. In addition, with template types, the friend strategy scales better. So I'd get used to using it, even if technically "less permissions is better".


1 I find this annoyingly asymmetrical, so I prefer non-member < to member <.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
3

Given the code you've shown, you're pretty close, but not quite there ... you need to distinguish between a member function and a stand-alone "free" function.

struct myStruct final { int id; };
inline bool operator<(const myStruct& lhs, const myStruct& rhs) {
   return lhs.id < rhs.id;
}

Keep in mind that you should prefer to use non-member functions.


Using a member function is similar, but less desirable than above:

class myStruct final
{
    int id_;
public:
    bool operator<(const myStruct& rhs) const {
       return id_ < rhs.id_;
    }
};

Another way would be to make operator<() a friend function, which has various tradeoffs; but your original code doesn't show that technique.

Community
  • 1
  • 1
Ðаn
  • 10,934
  • 11
  • 59
  • 95
  • Seems to me like non-member functions are only preferable as long as they aren't friend. Otherwise they are equivalent to member functions in terms of encapsulation. In fact, the accepted and favored answer in the very question you link concludes that you should only make functions friends when they can't be members. Can you provide an alternative argument for that point? – François Andrieux May 11 '17 at 19:16
  • My point is that you say "prefer to use non-member functions" and support that statement with a link to another question, but the link only supports the case where that non-member is non-friend, which is not applicable here. For the link to be applicable, it should say "prefer to use non-member non-friend functions" but then it wouldn't be relevant to your answer. Essentially, I'm wondering if you can enlighten me and future readers on why is a friend non-member function is more desirable than a member function. – François Andrieux May 11 '17 at 19:27