3

It's the first time I use the std::set container and I have a problem with the operator std::less.

I declare the set :

std::set<MyClass*, std::less<MyClass> > _set;

Then, I overload the operator< for MyClass ; the problem seems to be linked the mix between class and pointer, because I have this error message :

no match for call to '(std::less<MyClass>) (MyClass *const&, MyClass *const&)'
Griwes
  • 8,805
  • 2
  • 43
  • 70
federem
  • 299
  • 1
  • 6
  • 17
  • Any design using containers of raw pointers is broken. Your problem is just another proof of that, since it confuses even you. – Griwes Nov 11 '13 at 11:41
  • just `std::set myClassSet;` – billz Nov 11 '13 at 11:43
  • 1
    @Griwes That's just wrong. `std::map` with a raw pointer as the mapped type are one of the more frequent uses of `std::map`. – James Kanze Nov 11 '13 at 11:47
  • 1
    You can't use `std::less`, as it is not a function designed to compare `MyCLass*`s. Instead, you should write your own comparison function... `struct Cmp { bool operator()(const MyClass* p_lhs, const MyClass* p_rhs) const { return *p_lhs < *p_rhs; } };` and specify it as the second template parameter to the `set`. – Tony Delroy Nov 11 '13 at 11:50
  • @Griwes: it's a question of ownership and lifetime... if you know you're doing some transient indexing operation without "owning" the data or risk of pointed-to data being invalidated, it can be best to use raw pointers. – Tony Delroy Nov 11 '13 at 11:51
  • @JamesKanze, citation needed. – Griwes Nov 11 '13 at 11:54
  • @TonyD, the question itself does not indicate that he knows that. OP said: "it's the first time I use the std::set container". For me it's quite obvious he just wants a set of either objects or smart pointers; nowhere he stated he wants to keep already existing objects in the set. – Griwes Nov 11 '13 at 11:55
  • @Griwes: that's a fair perspective... if federem's an experienced C programmer - for example - might understand the issues and be making a conscious choice to reference existing data, but otherwise by-value storage is likely wanted. As per my comment on your answer - your smart_pointer proposal does order the data differently though - that may or may not be significant. – Tony Delroy Nov 11 '13 at 12:00
  • @TonyD, yes, hence my edit after your comment. – Griwes Nov 11 '13 at 12:09
  • @Griwes Don't be silly. Why would he want pointers in a set _unless_ the objects exist elsewhere? (In practice, there are very few cases where you would want smart pointers in a container.) – James Kanze Nov 11 '13 at 18:05
  • @JamesKanze, maybe because he has came here from Java, where you `new` everything? Or maybe he has learned what he knows from one of those books which teach to `new` and `delete` everything? And I couldn't disagree more with your last statement. – Griwes Nov 11 '13 at 18:11
  • Maybe @Griwes has jumped on board the "never use raw pointers" bandwagon without actually being able to back up that mantra for any specific use case... – Lightness Races in Orbit Nov 11 '13 at 18:15
  • 2
    @Griwes Off hand (and without seeing anything else of his code), I do suspect that the correct answer here is to use values. Because of languages like Java, a large number of C++ beginners do use dynamic allocation when they shouldn't. The correct answer in such cases _is_ to use values, and not to simulate Java's garbage collection with smart pointers. And once you've eliminated those cases, the case for smart pointers pretty much disappears. Almost all uses of the associative containers with a pointer are for navigation. – James Kanze Nov 12 '13 at 09:16
  • @JamesKanze, I mostly agree. Since R.Martinho noticed me you cannot specialize `std::less` for `std::xxx_ptr`, I removed that part of the answer. I also agree that in the vast majority of cases, a value is what should be used. Nevertheless, the question originally involved pointers, and didn't say anything about `MyClass`, so there is no way to know if it's polymorphic, so I decided to originally include case involving pointers in my answer. Since OP didn't say anything about polymorphicness of `MyClass` in the meantime, I removed it. – Griwes Nov 12 '13 at 09:59

2 Answers2

4

You overloaded operator< for MyClass, but your set has pointers to MyClass.

Simple answer is not to use pointers. If you feel you must use pointers, then the answer is to write a custom comparator for your set.

struct Comp
{
    bool operator()(MyClass* x, MyClass* y);
};

std::set<MyClass*, Comp> _set;
john
  • 85,011
  • 4
  • 57
  • 81
2

You want to use just std::set<MyClass>. The comparator is defaulted to std::less.

Griwes
  • 8,805
  • 2
  • 43
  • 70
  • That very much depends on what the `set` is used for. If the reason for using pointers is that the objects live elsewhere, then he has to use raw pointers. (I don't think I've come across a case where one would put smart pointers in a `set`.) – James Kanze Nov 11 '13 at 11:49
  • (http://en.cppreference.com/w/cpp/memory/shared_ptr/operator_cmp) "Note that the comparison operators for shared_ptr simply compare pointer values; the actual objects pointed to are not compared." <-- your smart_ptr advice won't keep the objects in the same order as element comparison. – Tony Delroy Nov 11 '13 at 11:55
  • @JamesKanze, I just gave you a case where one would put smart pointers in a set - enablic polymorphism on the type. – Griwes Nov 11 '13 at 11:55
  • @Griwes But how often are non-entity types polymorphic? Given that he seems to be relatively new, suggesting value semantics is a good idea. But if value semantics don't apply, suggesting smart pointers to someone relatively new is a bad idea; there are really very few cases where they are appropriate. – James Kanze Nov 11 '13 at 18:07
  • @JamesKanze, on the contrary; introducing *raw* pointers to someone relatively new is a bad idea. Introducing smart pointers before raw pointers and after containers (and since this question is about `std::set`, I'd say we have already reached this point) is the sane way to teach the language. – Griwes Nov 11 '13 at 18:14
  • @Griwes No. The smart pointers cover a few special uses only. They could even be omitted. (I don't think Stroustrup even mentions them in his _Programming: Principles and Practice_, which is by far the best learning text for C++.) Where as as soon as you start speaking of entity objects, you need to introduce raw pointers. – James Kanze Nov 12 '13 at 09:20
  • @JamesKanze, PPP is dated (published in 2009, two years before C++11 was standardized, and I don't think he describes Boost there). See the quote [here](http://www.stroustrup.com/Programming/): "Note this is not the 4th edition of The C++ Programming Language and it uses the current ISO standard C++, C++98, rather than C++11.". The book you should be looking at is "TC++PL, 4th edition", which [obviously mentions smart pointers, since they follow the basic RAII principle](http://www.stroustrup.com/4thContents.html) (5.2.1, called "`unique_ptr` and `shared_ptr`" in published book, and 34.3). – Griwes Nov 12 '13 at 09:43
  • @Griwes PPP is still the best basic text for learning C++. And nothing which was added to C++11 is essential for good C++ (although some things, like lambdas, make it more convenient). TC++PL is a reference manual (and so obviously will mention everything, regardless of how essential). But you've just argued my point: smart pointers implement RAII (and nothing else), and as such, only make sense as local variables. – James Kanze Nov 12 '13 at 10:02
  • @JamesKanze, so tell me how exactly do you implement a resource with shared ownership without `std::shared_ptr`, or put a polymorphic object into a container without `std::unique_ptr` (well, I do know answer to this second one - create a wrapper that behaves like a value; but I'd argue that it can generate enough boilerplate to make it not really worth it). And since the moment we have move semantics, we can move RAII handles out and make them live longer, so it's no longer only about local variables. – Griwes Nov 12 '13 at 10:06
  • @Griwes No one is saying that there is never a use for smart pointers. But the use is a very specific one. Shared ownership of a resource is in itself very, very rare (but in specific, limited cases, `shared_ptr` can be used to make up for the lack of garbage collection in C++). Transferred ownership (using `std::unique_ptr`) is more frequent, and there are even cases where the transfer could go through a container, but they are also very special cases (the only ones I can think of involve threading). – James Kanze Nov 12 '13 at 14:25