Edit: I fixed my mistake: I'm using a set
and not a vector
.
Please consider the following example code:
set<Foo *> set_of_foos;
set_of_foos.insert(new Foo(new Bar("x")));
set_of_foos.insert(new Foo(new Bar("y")));
[...]
// The way a "foo" is found is not important for the example.
bool find_foo(Foo *foo) {
return set_of_foos.end() != set_of_foos.find(foo);
}
Now when I call:
find_foo(new Foo(new Bar("x")));
the function returns false
since what I'm looking for can't be found. The reason is obvious to me: The pointers point to different objects since they are allocated both with a new
, resulting in different values of the addresses.
But I want to compare the contents of Foo
(i.e. "x"
in the above example) and not Foo *
itself. Using Boost is not an option as well as modifying Foo
.
Do I need to loop through each of the Foo *
inside set_of_foos
or is there a simpler solution? I tried uniquely serializing the contents of each Foo
and replace the set<Foo *>
with a map<string, Foo *>
, but this seems like a very "hacked" solution and not very efficient.
I had the same question and ended up writing a simple DereferenceCompare class to do the job. I'd be curious to know what others think of this. At the crux of the problem is that the existing answers require the programmer using your set to access it in an unusual way that is prone to leaking memory, i.e. by passing an address of a temporary to
std::set::find()
or throughstd::find_if()
. What's the point of using a standard container if you're going to access it in a non-standard way? Boost has a good container library that solves this problem. But since transparent comparators were introduced in C++14 you can write a custom comparator that makesstd::set::insert()
andstd::set:find()
work as expected without depending on Boost. You could use it as something likestd::set< Foo*, DereferenceCompare<Foo, YourFooComparator> > set_of_foos;
You do need to loop to find what you want, but you can use std::find_if or another "wrapped loop". This is more natural with lambdas in C++0x, but in C++03 I'd just use a regular for loop, possibly wrapped in your own function if you need to do this in more than one place.
C++11
If you can make use of C++11 features, then you can also use a lambda expression instead of defining a comparison object, as shown in the other answers. To make the below example code working, I have defined
Bar
andFoo
from your code as follows:If you provide the below lambda expression as key comparison function to the
std::set
, then your content (i.e. the strings"x"
and"y"
) is compared instead of the pointers pointing to the content. Consequently, also thefind()
works as intended, as shown by the following code:Output:
Code on Ideone
Note: A
std::set
only allows unique entries (i.e. keys). Whether entries are unique is decided based on the provided key comparison function. For the code above this means, that you can only store a single entry withpBar->str == "x"
, even ifBar
orFoo
are stored at different adresses. If you want to store multiple entries withpBar->str == "x"
(for example), then you have to use astd::multiset
.You may consider also using the Boost Ptr container library. It allows having a list of pointers using standard algorithms, find, etc. as if it contained objects, and automatically releasing the memory used by the pointers upon vector deletion.
Change your
vector
toset
with your custom comparable function to compareFoo
objects.Should be:
Instead of using std::find, use std::find_if and provide your own predicate. This of course relies in you being able to access the member that holds "x" in Foo.
If you can't access the member and you can guarantee that all other members will be the same, you could try a bare memcmp in the above functor rather than "==".