Ordered associative containers with identity-based equality - c++

I am writing a template function, with template argument X. Inside that function, I want to create std::set<std::pair<int, X>> such that:
objects in that set are sorted by the first (int) field of the pair (I don't care about how ties are broken)
I can add multiple objects with the same .first to the set, as long as their .second are not identical
If I knew that template argument X always has < defined, the most basic std::set<std::pair<int, X> (with the default comparator) would have worked perfectly fine. Unfortunately, I cannot assume anything about X.
I was thinking to "cheat" and use pointer-based comparison for the X field:
template <typename X>
struct DistCmp {
bool operator()(const std::pair<int, X>& lhs, const std::pair<int, X>& rhs) const {
return lhs.first < rhs.first || lhs.first == rhs.first && &lhs.second < &rhs.second;
}
};
template <typename X>
void f() {
std::set<std::pair<int, X>, DistCmp<X>> s{};
// ...
}
(After all, I don't really care how the .second is compared, as long as it's not compared equally for non-identical objects.)
Unfortunately, I don't think it's correct. Partly because of the quote from C++ standard here (it suggests that pointer comparison, in general, is unspecified, so I can't rely on them being unequal for non-identical objects). And partly I just feel it's suspicious/hacky.
Is there any clean/portable solution for this issue?
Update:
One approach I thought about was to use == for the comparison of pointers instead of <. However, this is no good because it will cause pair<1, x> < pair<1, y> && pair<1, y> < pair<1, x> to be true. This violates the requirement of strict weak ordering and may cause stuff to break.

Short answer: use std::multiset and sidestep whole issue - multiset allows multiple keys, so just compare on first element of pair.
Note, that if you want to disallow the same VALUE of X in your map, you've to add requirement to X, so it were at least EqualityComparable. Otherwise you can't even detect, when values of X are the same and when are different.
Longer answer:
Your code won't produce the result you're hoping for. Consider adding new pair <0, x> to map with <0, x>. std::map will try to find existence of <0, x> using temporary copy of <0, x>. It will use address of x (temporary!), it will compare false to everything, that is inside in map and std::map will find a place to insert based on address of x (temporary!). Then it will COPY x, thus changing address and possibly breaking its own ordering.

I think ::std::map< int, ::std::set< X > > satisfies both of your criteria.
Update: since there might be no < operator to compare X instance you may want to write a template with specialization:
#include <map>
#include <set>
#include <vector>
#include <type_traits>
#include <algorithm>
#include <utility>
// Default implementation is used when there is no < operator to compare T instances,
// but there is == operator.
template< typename T, typename TDummy = void >
t_FancyContainerHelper final
{
public: using
t_Values = ::std::vector< T >;
public: using
t_IntToValues = ::std::map< int, t_Values >;
public: static void
Insert(t_IntToValues & int_to_values, int const key, T const & value)
{
auto p_pair{int_to_values.find(key)};
if(int_to_values.end() != p_pair)
(
auto const & values(p_pair->second);
if(values.end() != ::std::find(values.begin(), values.end(), value))
{
return;
}
}
else
{
p_pair = int_to_values.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(key)
, ::std::forward_as_tuple()
).second;
}
auto & values(p_pair->second);
values.emplace(value);
}
};
// This specialization is used when there is < operator to compare T instances.
template< typename T > class
t_FancyContainerHelper
<
T
, ::std::enable_if_t
<
::std::is_same
<
bool
, decltype
(
::std::declval< T const & >()
<
::std::declval< T const & >()
)
>
>
> final
{
public: using
t_Values = ::std::set< T >;
public: using
t_IntToValues = ::std::map< int, t_Values >;
public: static void
Insert(t_IntToValues & int_to_values, int const key, T const & value)
{
auto p_pair{int_to_values.find(key)};
if(int_to_values.end() != p_pair)
(
auto const & values(p_pair->second);
if(values.end() != values.find(value))
{
return;
}
}
else
{
p_pair = int_to_values.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(key)
, ::std::forward_as_tuple()
).second;
}
auto & values(p_pair->second);
values.emplace(value);
}
};

Related

Is there a more idiomatic way to specialise behaviour using flags passed via template?

Apologises for the ambiguous title.
Here is my code:
struct LowHigh
{
};
struct HighLow
{
};
template < class LookupScheme>
struct ladder_base
{
using value_type = price_depth;
using ladder_type = std::vector< value_type >;
template < class T >
struct lookup;
template <>
struct lookup< LowHigh >
{
static constexpr auto func = std::upper_bound< ladder_type::iterator, value_type >;
};
template <>
struct lookup< HighLow >
{
static constexpr auto func = std::lower_bound< ladder_type::iterator, value_type >;
};
void
insert(value_type v)
{
auto iter = lookup< LookupScheme >::func(std::begin(data_), std::end(data_), v);
data_.insert(iter, std::move(v));
}
protected:
std::vector< value_type > data_;
};
} // namespace detail
struct bid_ladder : detail::ladder_base< detail::HighLow >
{
};
struct offer_ladder : detail::ladder_base< detail::LowHigh >
{
};
I'm specialising lookup::func depending on the scheme passed as a template type. There are currently only two possible schemes: LowHigh & HighLow. This has the effect of determining how the underlying vector is sorted.
Is there a more idiomatic/cleaner way to express this logic?
These algorithms take a comparison object as their last parameter - so you can use that to your advantage.
template < class Compare >
struct ladder_base
{
using value_type = price_depth;
using ladder_type = std::vector< value_type >;
void
insert(value_type v)
{
auto iter = std::upper_bound(data_.begin(), data_.end(), v, Compare{} );
data_.insert(iter, std::move(v));
}
protected:
std::vector< value_type > data_;
};
And then use ladder_base<std::less<>> or ladder_base<std::greater<>>, depending on which sort order you want.
Note that std::lower_bound and std::upper_bound are not antonyms, so your original wasn't really correct. lower_bound gives you the first element >= x and upper_bound gives you the first element > x. So changing from one to the other doesn't change your sort order (both require increasing order), only the comparison object affects that.
For instance:
std::vector<int> v = {1, 3, 5, 7};
auto i = std::lower_bound(v.begin(), v.end(), 3); // this is the 3
auto j = std::upper_bound(v.begin(), v.end(), 3); // this is the 5
Note that the vector is sorted in increasing order, but both calls are perfectly well-formed. If you wanted a reverse sort, you'd have to pass std::greater{} in as the comparison object (as I'm showing).
But either way, you want to use std::upper_bound - regardless of sort order.
The idiomatic way of doing that type of stuff is to have the template parameter contain the code to invoke directly, instead of indirecting through a tag type like you are doing.
Mind you, at that point, you could always just pass std::upper_bound directly as a template parameter.
Furthermore, since this is tagged c++20, you would also ideally use a concept, to constrain the types that can be passed to ladder_base.
#include <concepts>
#include <vector>
using price_depth = int;
template<typename T>
concept LookupScheme = requires (const T& x, const std::vector<price_depth>& v) {
{x(v.begin(), v.end(), price_depth{})} -> std::same_as<decltype(v.begin())>;
};
namespace detail {
struct LowHigh {
template<typename ForwardIt, typename T>
decltype(auto) operator()(ForwardIt first, ForwardIt last, const T& value ) const {
return std::upper_bound(first, last, value);
}
};
struct HighLow {
template<typename ForwardIt, typename T>
decltype(auto) operator()(ForwardIt first, ForwardIt last, const T& value ) const {
return std::lower_bound(first, last, value);
}
};
template <LookupScheme Scheme>
struct ladder_base
{
using value_type = price_depth;
using ladder_type = std::vector< value_type >;
void insert(value_type v)
{
auto iter = Scheme::exec(std::begin(data_), std::end(data_), v);
data_.insert(iter, std::move(v));
}
protected:
std::vector< value_type > data_;
};
} // namespace detail
struct bid_ladder : detail::ladder_base< detail::LowHigh >
{
};
struct offer_ladder : detail::ladder_base< detail::HighLow >
{
};
You can see the same approach used in the standard library's sorted containers, such a std::map<>'s Compare parameter.

Compare std::set with custom comparer

I am trying to use the std::tie to implement the operator< in order to create a map of structs that contain a set. The same code without templates seem that it works. I am getting this message code from my compiler:
/usr/include/c++/4.8/bits/stl_algobase.h:888: error: no match for operator< (operand types are const SiPa<int, int> and const SiPa<int, int>)
if (*__first1 < *__first2)
^
Everything compiles if I comment the myMap.insert({akey, true}); line.
Any hints?
template<class I = int, class S = int>
struct SiPa
{
I i;
S s;
};
template<class I = int, class S = int>
struct SiPaComparator
{
bool operator() (const SiPa<I, S>& first, const SiPa<I, S>& second) const
{
return std::tie(first.i, first.s) < std::tie(second.i, second.s);
}
};
template<class I = int, class S = int>
struct AKey
{
typedef std::set< SiPa<I, S>, SiPaComparator<I,S> > SetType;
SetType keySet;
I keyI;
};
template<class I = int, class S = int>
struct AKeyComparator
{
bool operator() (const AKey<I, S>& first, const AKey<I, S>& second) const
{
return std::tie(first.keySet, first.keyI) < std::tie(second.keySet, second.keyI);
}
};
int main()
{
AKey<int,int> akey;
std::map<AKey<int,int>, bool, AKeyComparator<int,int>> myMap;
myMap.insert({akey, true});
}
You need add operator< for struct SiPa, std::map require it
template<class I = int, class S = int>
struct SiPa {
I i;
S s;
bool operator<(const SiPa<I, S> &ref) {
return i < ref.i && s < ref.s;
}
};
In general, comparators on maps and sets are stateful. When comparing two different sets or maps, there is no obvious way to pick which one to use.
So when comparing different sets and maps via <, you get std::lexographical_compare with no Compare argument, which uses <. (Note this sucks for sets of pointers to objects not from the same array)
struct order_by_tie {
template<class Lhs, class Rhs,
class=std::enable_if_t<
std::is_base_of<order_by_tie, Lhs>::value
&& std::is_base_of<order_by_tie, Rhs>::value
>
>
friend bool operator<(Lhs const& lhs, Rhs const& rhs) {
return as_tie(lhs) < as_tie(rhs);
}
};
order_by_tie is intended to be inherited from. It uses ADL (argument dependent lookup) to enable < on its descendent classes, implemented by calling the free function as_tie on each side then doing a <.
We use it as follows:
template<class I = int, class S = int>
struct SiPa:order_by_tie
{
I i;
S s;
friend auto as_tie( SiPa const& self ) {
return std::tie(self.i, self.s);
}
};
template<class I = int, class S = int>
struct AKey:order_by_tie
{
typedef std::set< SiPa<I, S>, SiPaComparator<I,S> > SetType;
SetType keySet;
I keyI;
friend auto as_tie( AKey const& self ) {
return std::tie(self.keySet, self.keyI);
}
};
then
std::map<AKey<int,int>, bool> myMap;
works.
as_tie uses C++14, because the alternative is annoying. You can add a -> decltype(std::tie( blah, blah )) for C++11 (repeating yourself).
According to http://www.cplusplus.com/reference/set/set/operators/
The other operations also use the operators == and < internally to compare the elements, behaving as if the following equivalent operations were performed:
Notice that none of these operations take into consideration the internal comparison object of neither container.
So the comparaison of std::set<SiPa<I, S>, SiPaComparator<I,S>> is done with
operator < (const SiPa<I, S>&, const SiPa<I, S>&)
and not with
SiPaComparator<I, S>{}
The workaround is to define that operator <.

Comparing std::tuple (or std::pair) of custom types who has alternative orderings. Is it possible to plug-in a custom less-than / comparison function?

The Problem
I have a custom type A who has natural ordering (having operator<) and multiple alternative orderings (case-sensitive, case-insensitive, etc.). Now I have a std::pair (or std::tuple) consisting (one or more of) A. Here are some examples of types I want to compare: std::pair<A, int>, std::pair<int, A>, std::tuple<A, int, int>, std::tuple<int, A, int>. How can I compare the std::pair (or std::tuple) using the default element-wise comparison implementation, plugging-in my comparison function for A?
The Code
The code below doesn't compile:
#include <utility> // std::pair
#include <tuple> // std::tuple
#include <iostream> // std::cout, std::endl
struct A
{
A(char v) : value(v) {}
char value;
};
// LOCATION-1 (explained in the text below)
int main()
{
std::cout
<< "Testing std::pair of primitive types: "
<< (std::pair<char, int>('A', 1)
<
std::pair<char, int>('a', 0))
<< std::endl;
std::cout
<< "Testing std::tuple of primitive types: "
<< (std::tuple<char, int, double>('A', 1, 1.0)
<
std::tuple<char, int, double>('a', 0, 0.0))
<< std::endl;
// This doesn't compile:
std::cout
<< "Testing std::pair of custom types: "
<< (std::pair<A, int>('A', 1)
<
std::pair<A, int>('a', 0))
<< std::endl;
return 0;
}
It is because operator< isn't defined for struct A. Adding it to LOCATION-1 above would solve the problem:
bool operator<(A const& lhs, A const& rhs)
{
return lhs.value < rhs.value;
}
Now, we have an alternative ordering for struct A:
bool case_insensitive_less_than(A const& lhs, A const& rhs)
{
char const lhs_value_case_insensitive
= ('a' <= lhs.value && lhs.value <= 'z'
? (lhs.value + 0x20)
: lhs.value);
char const rhs_value_case_insensitive
= ('a' <= rhs.value && rhs.value <= 'z'
? (rhs.value + 0x20)
: rhs.value);
return lhs_value_case_insensitive < rhs_value_case_insensitive;
}
Supposed we want to keep the original operator< for struct A (the case-sensitive one), how can we compare std::pair<A, int> with this alternative ordering?
I know that adding a specialized version of operator< for std::pair<A, int> solves the problem:
bool operator<(std::pair<A, int> const& lhs, std::pair<A, int> const& rhs)
{
return (case_insensitive_less_than(lhs.first, rhs.first)
? true
: case_insensitive_less_than(rhs.first, lhs.first)
? false
: (lhs.second < rhs.second));
}
However, I consider this a sub-optimal solution.
Firstly, for std::pair, it is easy to re-implement the element-wise comparison, but for std::tuple it might be complicated (dealing with variadic templates) and error-prone.
Secondly, I can hardly believe that it is the best-practice way to solve the problem: imagine that we have to define a specialized version of operator< for each of the following classes: std::tuple<A, int, int>, std::tuple<int, A, int>, std::tuple<int, int, A>, std::tuple<A, A, int>, ... (It's not even a practical way!)
Re-using the well written built-in operator< for std::tuple and plugging-in my less-than for struct A would be what I want. Is it possible? Thanks in advance!
The easy way would be to manually write compare( tup, tup, f ) that uses f to lexographically compare the elements in the tuples. But that is boring.
// This type wraps a reference of type X&&
// it then overrides == and < with L and E respectively
template<class X, class L, class E>
struct reorder_ref {
using ref = reorder_ref;
X&& x;
friend bool operator<(ref lhs, ref rhs) {
return L{}((X&&) lhs.x, (X&&) rhs.x);
}
friend bool operator==(ref lhs, ref rhs) {
return E{}((X&&) lhs.x, (X&&) rhs.x);
}
// other comparison ops based off `==` and `<` go here
friend bool operator!=(ref lhs, ref rhs){return !(lhs==rhs);}
friend bool operator>(ref lhs, ref rhs){return rhs<lhs;}
friend bool operator<=(ref lhs, ref rhs){return !(lhs>rhs);}
friend bool operator>=(ref lhs, ref rhs){return !(lhs<rhs);}
reorder_ref(X&& x_) : x((X&&) x_) {}
reorder_ref(reorder_ref const&) = default;
};
the above is a reference that changes how we order.
// a type tag, to pass a type to a function:
template<class X>class tag{using type=X;};
// This type takes a less than and equals stateless functors
// and takes as input a tuple, and builds a tuple of reorder_refs
// basically it uses L and E to compare the elements, but otherwise
// uses std::tuple's lexographic comparison code.
template<class L, class E>
struct reorder_tuple {
// indexes trick:
template<class Tuple, class R, size_t... Is>
R operator()(tag<R>, std::index_sequence<Is...>, Tuple const& in) const {
// use indexes trick to do conversion
return R( std::get<Is>(in)... );
}
// forward to the indexes trick above:
template<class... Ts, class R=std::tuple<reorder_ref<Ts const&, L, E>...>>
R operator()(std::tuple<Ts...> const& in) const {
return (*this)(tag<R>{}, std::index_sequence_for<Ts...>{}, in);
}
// pair filter:
template<class... Ts, class R=std::pair<reorder_ref<Ts const&, L, E>...>>
R operator()(std::pair<Ts...> const& in) const {
return (*this)(tag<R>{}, std::index_sequence_for<Ts...>{}, in);
}
};
the above stateless function object takes some new less and equals operations, and maps any tuple to a tuple of reorder_ref<const T, ...>, which change the ordering to follow L and E respectively.
This next type does what std::less<void> does for std::less<T> sort of -- it takes a type-specific stateless ordering function template object, and makes it a type-generic stateless ordering function object:
// This takes a type-specific ordering stateless function type, and turns
// it into a generic ordering function type
template<template<class...> class order>
struct generic_order {
template<class T>
bool operator()(T const& lhs, T const& rhs) const {
return order<T>{}(lhs, rhs);
}
};
so if we have a template<class T>class Z such that Z<T> is an ordering on Ts, the above gives you a universal ordering on anything.
This next one is a favorite of mine. It takes a type T, and orders it based on a mapping to a type U. This is surprisingly useful:
// Suppose there is a type X for which we have an ordering L
// and we have a map O from Y->X. This builds an ordering on
// (Y lhs, Y rhs) -> L( O(lhs), O(rhs) ). We "order" our type
// "by" the projection of our type into another type. For
// a concrete example, imagine we have an "id" structure with a name
// and age field. We can write a function "return s.age;" to
// map our id type into ints (age). If we order by that map,
// then we order the "id" by age.
template<class O, class L = std::less<>>
struct order_by {
template<class T, class U>
bool operator()(T&& t, U&& u) const {
return L{}( O{}((T&&) t), O{}((U&&) u) );
}
};
Now we glue it all together:
// Here is where we build a special order. Suppose we have a template Z<X> that returns
// a stateless order on type X. This takes that ordering, and builds an ordering on
// tuples based on it, using the above code as glue:
template<template<class...>class Less, template<class...>class Equals=std::equal_to>
using tuple_order = order_by< reorder_tuple< generic_order<Less>, generic_order<Equals> > >;
tuple_order does most of the work for us. All we need is to provide it with an element-wise ordering template stateless function object. tuple_order will then produce a tuple ordering functor based on it.
// Here is a concrete use of the above
// my_less is a sorting functiont that sorts everything else the usual way
// but it sorts Foo's backwards
// Here is a toy type. It wraps an int. By default, it sorts in the usual way
struct Foo {
int value = 0;
// usual sort:
friend bool operator<( Foo lhs, Foo rhs ) {
return lhs.value<rhs.value;
}
friend bool operator==( Foo lhs, Foo rhs ) {
return lhs.value==rhs.value;
}
};
template<class T>
struct my_less : std::less<T> {};
// backwards sort:
template<>
struct my_less<Foo> {
bool operator()(Foo const& lhs, Foo const& rhs) const {
return rhs.value < lhs.value;
}
};
using special_order = tuple_order< my_less >;
and bob is your uncle (live example).
special_order can be passed to a std::map or std::set, and it will order any tuples or pairs encountered with my_less replacing the default ordering of the elements.

c++ overloading operator[] for std::pair

I work a lot with pairs of values: std::pair<int, int> my_pair. Sometimes I need to perform the same operation on both my_pair.first and my_pair.second.
My code would be much smoother if I could do my_pair[j] and loop over j=0,1.
(I am avoiding using arrays because I don't want to bother with allocating memory, and I use pair extensively with other things).
Thus, I would like to define operator[] for std::pair<int, int>.
And I can't get it to work, (I'm not very good with templates and such)...
#include <utility>
#include <stdlib.h>
template <class T1> T1& std::pair<T1, T1>::operator[](const uint &indx) const
{
if (indx == 0)
return first;
else
return second;
};
int main()
{
// ....
return 0;
}
fails to compile. Other variations fail as well.
As far as I can tell, I am following the Stack Overflow operator overloading FAQ, but I guess I am missing something...
you cannot overload operator[] as a non-member
you cannot define a member function which has not been declared in the class definition
you cannot modify the class definition of std::pair
Here's a non-member implementation:
/// #return the nth element in the pair. n must be 0 or 1.
template <class T>
const T& pair_at(const std::pair<T, T>& p, unsigned int n)
{
assert(n == 0 || n == 1 && "Pair index must be 0 or 1!");
return n == 0 ? p.first: p.second;
}
/// #return the nth element in the pair. n must be 0 or 1.
template <class T>
T& pair_at(std::pair<T, T>& p, unsigned int index)
{
assert(index == 0 || index == 1 && "Pair index must be 0 or 1!");
return index == 0 ? p.first: p.second;
}
// usage:
pair<int, int> my_pair(1, 2);
for (int j=0; j < 2; ++j)
++pair_at(my_pair, j);
Note that we need two versions: one for read-only pairs and one for mutable pairs.
Don't be afraid to use non-member functions liberally. As Stroustrup himself said, there is no need to model everything with an object or augment everything through inheritance. If you do want to use classes, prefer composition to inheritance.
You can also do something like this:
/// Applies func to p.first and p.second.
template <class T, class Func>
void for_each_pair(const std::pair<T, T>& p, Func func)
{
func(p.first);
func(p.second);
}
/// Applies func to p.first and p.second.
template <class T, class Func>
void for_each_pair(std::pair<T, T>& p, Func func)
{
func(p.first);
func(p.second);
}
// usage:
pair<int, int> my_pair(1, 2);
for_each_pair(my_pair, [&](int& x){
++x;
});
That isn't too unwieldy to use if you have C++11 lambdas and is at least a bit safer since it has no potential to access out of bounds.
You cannot add functions to an existing class like this. And you certainly can't do it to things in the std namespace.
So you should define your own wrapper class:
class MyPair {
private:
std::pair<int,int> p;
public:
int &operator[](int i) { return (i == 0) ? p[0] : p[1]; }
// etc.
};
You probably should check out Boost.Fusion. You can apply algorithms to sequences(which std::pair is considered a sequence). So for example you can do for_each like this:
std::pair<int, int> my_pair;
for_each(my_pair, [] (int i)
{
cout << i;
});
You can also access the index of the element like this:
int sum = at_c<0>(my_pair) + at_c<1>(my_pair);

How can I find an element in a set which contains pointers to the elements?

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.
Change your vector to set with your custom comparable function to compare Foo objects.
Should be:
struct ltFoo
{
bool operator()(Foo* f, Foo* s) const
{
return f->value() < s->value();
}
};
set<Foo*, ltFoo> sFoo;
sFoo.insert(new Foo(new Bar("x"));
sFoo.insert(new Foo(new Bar("y"));
if (sFoo.find(new Foo(new Bar("y")) != sFoo.end())
{
//exists
}
else
{
//not exists
}
find_foo(new Foo(new Bar("x"))); does not sound like a good idea - it will most likely (in any scenario) lead to memory leak with that search function.
You could use find_if with a functor:
struct comparator {
Foo* local;
comparator(Foo* local_): local(local_) {}
~comparator() { /* do delete if needed */ }
bool operator()(const Foo* other) { /* compare local with other */ }
};
bool found = vec.end() != std::find_if(vec.begin(), vec.end(), comparator(new Foo(...)));
Do I need to loop through each of the Foo * inside vector_of_foos or is there a simpler solution?
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.
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.
struct FooBar
{
FooBar(Foo* search) : _search(search){}
bool operator(const Foo* ptr)
{
return ptr->{access to member} == _search->{access to member};
}
Foo* _search;
}
vector<Foo*>::iterator it = std::find_if(vec.begin(), vec.end(), FooBar(new Foo(new Bar("x")));
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 "==".
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.
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 through std::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 makes std::set::insert() and std::set:find() work as expected without depending on Boost. You could use it as something like std::set< Foo*, DereferenceCompare<Foo, YourFooComparator> > set_of_foos;
#ifndef DereferenceCompare_H
#define DereferenceCompare_H
#include <type_traits>
// Comparator for std containers that dereferences pointer-like arguments.
// Useful for containers of pointers, smart pointers, etc. that require a comparator.
// For example:
// std::set< int*, DereferenceCompare<int> > myset1;
// int myint = 42;
// myset1.insert(&myint);
// myset1.find(&myint) == myset.end(); // false
// myset1.find(myint) == myset.end(); // false
// myset1.find(42) == myset.end(); // false
// myset1.find(24) == myset.end(); // true, 24 is not in the set
// std::set<int*> myset2;
// myset2.insert(&myint); // compiles, but the set will be ordered according to the address of myint rather than its value
// myset2.find(&myint) == myset.end(); // false
// myset2.find(a) == myset.end(); // compilation error
// myset2.find(42) == myset.end(); // compilation error
//
// You can pass a custom comparator as a template argument. It defaults to std::less<T>.
// The type of the custom comparator is accessible as DereferenceCompare::compare.
// For example:
// struct MyStruct { int val; };
// struct MyStructCompare { bool operator() (const MyStruct &lhs, const MyStruct &rhs) const { return lhs.val < rhs.val; } };
// std::set< MyStruct*, DereferenceCompare<MyStruct, MyStructCompare> > myset;
// decltype(myset)::key_compare::compare comparator; // comparator has type MyStructCompare
template< typename T, class Compare = std::less<T> > class DereferenceCompare
{
#if __cplusplus==201402L // C++14
private:
// Less elegant implementation, works with C+=14 and later.
template<typename U> static constexpr auto is_valid_pointer(int) -> decltype(*(std::declval<U>()), bool()) { return std::is_base_of<T, typename std::pointer_traits<U>::element_type>::value || std::is_convertible<typename std::remove_cv<typename std::pointer_traits<U>::element_type>::type, T>::value; }
template<typename U> static constexpr bool is_valid_pointer(...) { return false; }
public:
template<typename U, typename V> typename std::enable_if<is_valid_pointer<U>(0) && is_valid_pointer<V>(0), bool>::type operator() (const U& lhs_ptr, const V& rhs_ptr) const { return _comparator(*lhs_ptr, *rhs_ptr); } // dereference both arguments before comparison
template<typename U, typename V> typename std::enable_if<is_valid_pointer<U>(0) && !is_valid_pointer<V>(0), bool>::type operator() (const U& lhs_ptr, const V& rhs) const { return _comparator(*lhs_ptr, rhs); } // dereference the left hand argument before comparison
template<typename U, typename V> typename std::enable_if<!is_valid_pointer<U>(0) && is_valid_pointer<V>(0), bool>::type operator() (const U& lhs, const V& rhs_ptr) const { return _comparator(lhs, *rhs_ptr); } // dereference the right hand argument before comparison
#elif __cplusplus>201402L // Better implementation, depends on void_t in C++17.
public:
// SFINAE type inherits from std::true_type if its template argument U can be dereferenced, std::false otherwise.
// Its ::value member is true if the type obtained by dereferencing U, i.e. the pointee, is either derived from T or convertible to T.
// Its ::value is false if U cannot be dereferenced, or it the pointee is neither derived from nor convertible to T.
// Example:
// DereferenceCompare<int>::has_dereference; // std::false_type, int cannot be dereferenced
// DereferenceCompare<int>::has_dereference<int>::is_valid_pointee; // false, int cannot be dereferenced
// DereferenceCompare<int>::has_dereference<int*>; // std::true_type, int* can be dereferenced to int
// DereferenceCompare<int>::has_dereference<int*>::is_valid_pointee; // true, dereferencing int* yields int, which is convertible (in fact, the same type as) int
// DereferenceCompare<int>::has_dereference< std::shared_ptr<int> >::is_valid_pointee; // true, the pattern also works with smart pointers
// DereferenceCompare<int>::has_dereference<double*>::is_valid_pointee; // true, double is convertible to int
// struct Base { }; struct Derived : Base { }; DereferenceCompare<Base>::has_dereference<Derived*>::is_valid_pointee; // true, Derived is derived from Base
// DereferenceCompare<int>::has_dereference<Derived*>; // std::true_type, Derived* can be dereferenced to Derived
// DereferenceCompare<int>::has_dereference<Derived*>::is_valid_pointee; // false, cannot convert from Derived to int nor does Derived inherit from int
template< typename, class = std::void_t<> > struct has_dereference : std::false_type { static constexpr bool is_valid_pointee = false; };
template< typename U > struct has_dereference< U, std::void_t<decltype(*(std::declval<U>()))> > : std::true_type { static constexpr bool is_valid_pointee = std::is_base_of<T, typename std::pointer_traits<U>::element_type>::value || std::is_convertible<typename std::remove_cv<typename std::pointer_traits<U>::element_type>::type, T>::value; };
template<typename U, typename V> typename std::enable_if<has_dereference<U>::is_valid_pointee && has_dereference<V>::is_valid_pointee, bool>::type operator() (const U& lhs_ptr, const V& rhs_ptr) const { return _comparator(*lhs_ptr, *rhs_ptr); } // dereference both arguments before comparison
template<typename U, typename V> typename std::enable_if<has_dereference<U>::is_valid_pointee && !has_dereference<V>::is_valid_pointee, bool>::type operator() (const U& lhs_ptr, const V& rhs) const { return _comparator(*lhs_ptr, rhs); } // dereference the left hand argument before comparison
template<typename U, typename V> typename std::enable_if<!has_dereference<U>::is_valid_pointee && has_dereference<V>::is_valid_pointee, bool>::type operator() (const U& lhs, const V& rhs_ptr) const { return _comparator(lhs, *rhs_ptr); } // dereference the right hand argument before comparison
#endif
public:
typedef /* unspecified --> */ int /* <-- unspecified */ is_transparent; // declaration required to enable polymorphic comparisons in std containers
typedef Compare compare; // type of comparator used on dereferenced arguments
private:
Compare _comparator;
};
#endif // DereferenceCompare_H
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 and Foo from your code as follows:
struct Bar {
Bar(std::string s) : str(s) {}
std::string str;
};
struct Foo {
Foo(Bar* p) : pBar(p) {}
Bar* pBar;
};
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 the find() works as intended, as shown by the following code:
int main() {
auto comp = [](const Foo* f1, const Foo* f2) { return f1->pBar->str < f2->pBar->str; };
std::set<Foo*, decltype(comp)> set_of_foos(comp);
set_of_foos.emplace(new Foo(new Bar("x")));
set_of_foos.emplace(new Foo(new Bar("y")));
auto it = set_of_foos.find(new Foo(new Bar("x")));
if (it == std::end(set_of_foos))
std::cout << "Element not found!" << std::endl;
else
std::cout << "Element found: " << (*it)->pBar->str << std::endl;
return 0;
}
Output:
Element found: x
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 with pBar->str == "x", even if Bar or Foo are stored at different adresses.
If you want to store multiple entries with pBar->str == "x" (for example), then you have to use a std::multiset.