State of the value to insert - c++

Is it possible the value passed to insert is left in a moved from state after move insertion if insert returns false?
#include <memory>
#include <map>
#include <cassert>
struct less
{
template< typename T >
bool operator () (const std::shared_ptr<T> & lhs, const std::shared_ptr<T> & rhs) const
{
return *lhs < *rhs;
}
};
int main() {
using key_type = int;
using value_type = int;
using map_type = std::map<std::shared_ptr<key_type>, std::shared_ptr<value_type>, less>;
map_type m;
auto p = typename map_type::value_type{std::make_shared<key_type>(1), std::make_shared<value_type>(1)};
if (!m.insert(p).second) {
assert(false);
}
assert(p.first);
assert(p.second);
if (m.insert(std::move(p)).second) {
assert(false);
}
assert(p.first);
assert(p.second);
}
Is the behavior of the last two assertion implementation defined?

From [map.modifiers/2] on std::map::insert, we have
template<class P>
pair<iterator, bool> insert(P&& x);
[...]
Effects: The first form is equivalent to return emplace(std::forward<P>(x)).
So it's in std::map::emplace... from [associative.reqmts/8] (emphasis mine):
a_­uniq.​emplace(args)
Effects: Inserts a value_­type object t constructed with std::forward<​Args​>(​args)... if and only if there is no element in the container with key equivalent to the key of t.
Hence, construction does not take place if there is already an object in the container that is associated with an equivalent key.
Let's verify with <map> from the Llvm implementation. In what follows, I've deleted some parts of the code to make it more readable. First, std::map::insert does this:
template <class _Pp, /* some SFINAE... */>
/* symbol visibility ... */
pair<iterator, bool> insert(_Pp&& __p)
{return __tree_.__insert_unique(_VSTD::forward<_Pp>(__p));}
Let's go to __tree::insert_unique, then:
pair<iterator, bool> __insert_unique(__container_value_type&& __v) {
return __emplace_unique_key_args(_NodeTypes::__get_key(__v), _VSTD::move(__v));
}
Still not there... but in __tree::emplace_unique_key_args it comes:
/* Template, template, template... return value... template */
__tree</* ... */>::__emplace_unique_key_args(_Key const& __k, _Args& __args)
{
__parent_pointer __parent;
__node_base_pointer& __child = __find_equal(__parent, __k);
__node_pointer __r = static_cast<__node_pointer>(__child);
bool __inserted = false;
if (__child == nullptr)
{
/* Some legacy dispatch for C++03... */
// THIS IS IT:
__node_holder __h = __construct_node(_VSTD::forward<_Args>(__args)...);
__insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
__r = __h.release();
__inserted = true;
}
return pair<iterator, bool>(iterator(__r), __inserted);
}
I think we don't have to look into __find_equal(__parent, __k) to understand that __child == nullptr is the condition that triggers the actual insertion. In this branch, the call to __construct_node forwards the arguments, which will steal the resources managed by the std::shared_ptr<int> you passed in. The other branch simply let's the arguments untouched.

Related

Using std::ranges algorithms with custom containers and iterators

I have the following simplified code representing a range of integers that I want to use with various std algorithms. I am trying to update my code to use C++20's ranges versions of the algorithms so I can delete all of the begin() and end() calls. In the below code, std::any_of works with my container and iterator, but std::ranges::any_of does not.
#include <iostream>
#include <algorithm>
class Number_Iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = int;
using difference_type = int;
using pointer = int*;
using reference = int&;
Number_Iterator(int start) noexcept : value(start) {}
Number_Iterator& operator++() noexcept { ++value; return *this; }
bool operator==(const Number_Iterator& other) const noexcept = default;
int operator*() const noexcept { return value; }
private:
int value;
};
class Numbers {
public:
Numbers(int begin, int end) noexcept : begin_value(begin), end_value(end) {}
Number_Iterator begin() const noexcept { return {begin_value}; }
Number_Iterator end() const noexcept { return {end_value}; }
private:
int begin_value;
int end_value;
};
int main() {
const auto set = Numbers(1, 10);
const auto multiple_of_three = [](const auto n) { return n % 3 == 0; };
// Compiles and runs correctly
if(std::any_of(set.begin(), set.end(), multiple_of_three)) {
std::cout << "Contains multiple of three.\n";
}
// Does not compile
if(std::ranges::any_of(set, multiple_of_three)) {
std::cout << "Contains multiple of three.\n";
}
return 0;
}
When I try to compile the above code, I get the following error messages from Visual Studio 2019 (16.11.15) with the flag /std:c++20:
Source.cpp(42,21): error C2672: 'operator __surrogate_func': no matching overloaded function found
Source.cpp(42,7): error C7602: 'std::ranges::_Any_of_fn::operator ()': the associated constraints are not satisfied
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include\algorithm(1191): message : see declaration of 'std::ranges::_Any_of_fn::operator ()'
I have tried looking at the std::ranges::_Any_of_fn::operator() declaration, but I find myself more confused by that.
What am I missing to get the std::ranges algorithms to work with my container?
For the curious, what I'm actually iterating over are squares on a chess board, but those are represented by integers, so the difference from the above code isn't so great.
To use your range with any_of it must satisfy the input_range concept:
template< class T >
concept input_range =
ranges::range<T> && std::input_iterator<ranges::iterator_t<T>>;
Then via the input_iterator concept:
template<class I>
concept input_iterator =
std::input_or_output_iterator<I> &&
std::indirectly_readable<I> &&
requires { typename /*ITER_CONCEPT*/<I>; } &&
std::derived_from</*ITER_CONCEPT*/<I>, std::input_iterator_tag>;
and via the input_or_output_iterator concept
template <class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> /*can-reference*/;
} &&
std::weakly_incrementable<I>;
you land in the weakly_incrementable concept:
template<class I>
concept weakly_incrementable =
std::movable<I> &&
requires(I i) {
typename std::iter_difference_t<I>;
requires /*is-signed-integer-like*/<std::iter_difference_t<I>>;
{ ++i } -> std::same_as<I&>; // pre-increment
i++; // post-increment
};
in which you see that the iterator must have both the pre-increment and post-increment versions of operator++.
The iterator must also be default constructible because std::ranges::end creates a sentinel:
template< class T >
requires /* ... */
constexpr std::sentinel_for<ranges::iterator_t<T>> auto end( T&& t );
And sentinel_for
template<class S, class I>
concept sentinel_for =
std::semiregular<S> &&
std::input_or_output_iterator<I> &&
__WeaklyEqualityComparableWith<S, I>;
requires it to satisfy semiregular:
template <class T>
concept semiregular = std::copyable<T> && std::default_initializable<T>;
But without being default constructible, this substitution will fail:
template < class T >
concept default_initializable = std::constructible_from<T> && requires { T{}; } && ...
Apparently, the std::ranges algorithms require two more methods in the iterator: a default constructor and a post-increment operator (return value optional). Adding these methods allows the code to compile and run correctly:
Number_Iterator() noexcept : value(-1) {}
void operator++(int) noexcept { ++value; }

How can I write a custom STL iterator for a class type without using raw pointers? Is there a practical advantage to this?

The title of this question used to be: Are there practical advantages to creating an iterator class compared to returning raw pointers from begin and end functions?
Recently I have been working on a code base which uses MFC and objects such as CArray<T, U>.
Some parts of new code which has been written make use of the STL and <algorithm> library.
For example
CArray<int int> carray;
carray // do stuff
std::vector<int> stlvector(begin(carray), end(carray));
stlvector.dostuff() // do stuff
I recently asked a question about creating iterators for a class such as CArray, which I do not have access to.
I now have some further questions about this. The first question can be found here. Here is my second question:
Should the begin and end functions return raw pointers or iterators?
In the linked question above, an example was provided as an answer which returns raw pointers. This answer was very similar to the implementation I used.
template<typename T, typename U>
auto begin(const CArray<T, U> &array>)
{
return &array[0];
}
template<typename T, typename U>
auto end(const CArray<T, U> &array>)
{
return (&array[array.GetCount() - 1]) + 1;
}
These functions return raw pointers. However I attempted to implement an iterator solution. So far I have not been successful.
The main reference which I used during my research can be found here:
https://internalpointers.com/post/writing-custom-iterators-modern-cpp
First attempt
This is the first attempt that I made in finding a solution.
You can play with this code here.
#include <iostream>
#include <iterator>
#include <algorithm>
template <typename U>
class CArrayForwardIt
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = U;
using pointer = U*;
using reference = U&;
public:
CArrayForwardIt(pointer ptr)
: m_ptr(ptr)
{
}
// = default?
//CArrayForwardIt(CArrayForwardIt<U> other)
// : m_ptr(ptr)
// {
// }
reference operator*() const
{
return *m_ptr;
}
// what does this do, don't understand why operator-> is needed
// or why it returns a U* type
pointer operator->()
{
return m_ptr;
}
CArrayForwardIt& operator++()
{
++ m_ptr;
return *this;
}
CArrayForwardIt operator++(int)
{
CArrayForwardIt tmp(*this);
++ (*this);
return tmp;
}
friend bool operator==(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
return lhs.m_ptr == rhs.m_ptr;
}
friend bool operator!=(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
return !(lhs == rhs);
}
private:
pointer m_ptr;
};
template<typename T, typename U>
auto begin(const CArray<T, U> &array)
{
return CArrayForwardIt<U>(&array[0]);
}
template<typename T, typename U>
auto end(const CArray<T, U> &array)
{
return CArrayForwardIt<U>((&array[array.GetCount() - 1]) + 1);
}
int main()
{
CArray<int, int> c;
// do something to c
std::vector<int> v(begin(c), end(c));
return 0;
}
This is what happens when I try to compile this (with Visual Studio 2019 Pro).
no instance of constructor "std::vector<_Ty, _Alloc>::vector [with _Ty=int, _Alloc=std::allocator<int>]" matches argument list
'<function-style-cast>': cannot convert from 'contt TYPE*' to 'std::CArrayForwardIt<U>'
'std::vector<int, std::allocator<int>>::vector(std::vector<int, std::allocator<int>> &&, const _Alloc &) noexcept(<expr>)': cannot convert from argument 1 from 'void' to 'const unsigned int'
Being more familiar with gcc, I have little knowledge of how to understand this.
Second attempt
I made another two further attempts at this but they were quite similar.
One was to change my class CArrayForwardIt to inherit from iterator<std::forward_iterator_tag, std::ptrdiff_t, U, U*, U&>, and to remove the using... lines at the top of the class. This didn't seem to get me any closer to a solution.
In addition, I looked at the constructor definition for std::vector. See here.
I may be misunderstanding here, but it looks like std::vector requires a InputIt type argument.
Therefore I tried to change my class to be something like this:
#include <iostream>
#include <iterator>
#include <algorithm>
template <typename U>
class forward_iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = U;
using pointer = U*;
using reference = U&;
public:
forward_iterator(pointer ptr)
: m_ptr(ptr)
{
}
// = default?
//forward_iterator(forward_iterator<U> other)
// : m_ptr(ptr)
// {
// }
reference operator*() const
{
return *m_ptr;
}
// what does this do, don't understand why operator-> is needed
// or why it returns a U* type
pointer operator->()
{
return m_ptr;
}
forward_iterator& operator++()
{
++ m_ptr;
return *this;
}
forward_iterator operator++(int)
{
forward_iterator tmp(*this);
++ (*this);
return tmp;
}
friend bool operator==(const forward_iterator& lhs, const forward_iterator& rhs)
{
return lhs.m_ptr == rhs.m_ptr;
}
friend bool operator!=(const forward_iterator& lhs, const forward_iterator& rhs)
{
return !(lhs == rhs);
}
private:
pointer m_ptr;
};
template<typename T, typename U>
auto begin(const CArray<T, U> &array)
{
return forward_iterator<U>(&array[0]);
}
template<typename T, typename U>
auto end(const CArray<T, U> &array)
{
return forward_iterator<U>((&array[array.GetCount() - 1]) + 1);
}
int main()
{
CArray<int, int> c;
// do something to c
std::vector<int> v(begin(c), end(c));
return 0;
}
This, perhaps unsurprisingly, did not compile either. At this point I became confused. std::vector appears to demand an InputIt type, which forward_iterator should work for, but it doesn't seem to make sense to redefine what forward_iterator is, even if I write this class outside of namespace std.
Question
I am fairly sure there should be a way to write an iterator class for the MFC CArray, which can be returned by begin and end functions. However, I am confused as to how to do this.
Further to the question of writing a working solution, I am beginning to wonder if there are any practical advantages to doing this? Does what I am trying to do even make sense? The raw pointer solution clearly works, so are there any advantages of investing the effort to write an iterator based solution? Can iterator solutions provide more sophisticated bounds checking, for example?
Since I managed to get this working I wanted to post a solution, hopefully I don't make too many errors transcribing it.
One thing that was not shown in the above code snippet is the fact that all these class and function definitions existed inside of namespace std. I posted another question about this earlier, and was informed that these things should not be inside namespace std. Correcting this seems to have resolved some problems and made the solution a step closer.
You can find that question here.
This is what I have so far: This is how to write an iterator for an external class which the programmer does not have access to. It also works for your own custom types or containers which you do have access to.
// How to write an STL iterator in C++
// this example is specific to the MFC CArray<T, U> type, but
// it can be modified to work for any type, note that the
// templates will need to be changed for other containers
#include <iterator>
#include <mfc stuff...>
template<typename T, typename U>
class CArrayForwardIt : public std::iterator<std::forward_iterator_tag, std::ptrdiff_t, U, U*, U&>
{
// the names used in this class are described in this list
// using iterator_category = std::forward_iterator_tag;
// using difference_type = std::ptrdiff_t;
// using value_type = U;
// using pointer = U*;
// using reference = U&;
public:
CArrayForwardIt(CArray<T, U> &array_ref, const std::size_t index)
: m_array_ref(array_ref)
, m_index(index)
{
}
// the only way I could get this to work was to make the return type
// an explicit U&, I don't know why this is required, as using
// reference operator*() const did not seem to work
U& operator*() const
{
if(m_index < m_array_ref.GetCount())
{
return m_array_ref[m_index];
}
else
{
throw std::out_of_range("Out of range Exception!");
}
}
CArrayForwardIt& operator++()
{
++ m_index;
return *this;
}
CArrayForwardIt operator++(int)
{
CForwardArrayIt tmp(*this);
++(*this);
}
friend bool operator==(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
if(&(lhs.m_array_ref) == &(rhs.m_array_ref))
{
return lhs.m_index == rhs.m_index;
}
return false;
}
friend bool operator!=(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
return !(lhs == rhs);
}
private:
std::size_t m_index;
CArray<T, U> &m_array_ref;
};
template<typename T, typename U>
auto begin(CArray<T, U> &array)
{
return CArrayForwardIt<T, U>(array, 0);
}
template<typename T, typename U>
auto end(CArray<T, U> &array)
{
return CArrayForwardIt<T, U>(array, array.GetCount());
}
int main()
{
CArray<int, int> array;
// do stuff to array
// construct vector from elements of array in one line
std::vector<int> vector(begin(array), end(array));
// also works with other STL algorithms
}
Note my comment about the U& operator* which produced some compiler error when written as reference operator* which might be a Visual Studio compiler bug. I'm not sure about this.
I would suggest that although this method is more difficult to implement (but not much when you know how to do it) it has the advantage of not using raw pointers which means that the iterator functions can provide proper exception throwing statements when illegal operations are attempted. For example, incrementing the iterator when it is already at the end.
Useful references:
https://lorenzotoso.wordpress.com/2016/01/13/defining-a-custom-iterator-in-c/
https://internalpointers.com/post/writing-custom-iterators-modern-cpp
For completeness, here is the simpler solution using raw pointers.
template<typename T, typename U>
auto begin(CArray<T, U> &array)
{
return &(array[0]);
}
template<typename T, typename U>
auto end(CArray<T, U> &array)
{
// get address of last element then increment
// pointer by 1 such that it points to a memory
// address beyond the last element. only works for
// storage containers where higher index elements
// are guaranteed to be at higher value memory
// addresses
if(array.GetCount() > 0)
{
return &(array[array.GetCount() - 1]) + 1;
}
else
{
return &(array[0]) + 1;
}
}
You can use these in the same way as demonstrated in the other answer, however there is also a way to use STL vector without the begin and end functions:
CArray<int, int> array; // ... do something to array
std::vector<int> vec(&array[0], &(array[array.GetCount() - 1]) + 1);
// note only works if elements guaranteed to be in continuous
// packed memory locations
but it also works with begin and end which is nicer
std::vector<int> vec(begin(array), end(array));

Parameter pack expansion doesn't work

I tried to make multiplex of std::set, named NDos::set_multiplex, which can view the elements in perspective of various comparison objects. For example, a set of playing cards could be sorted with rank first and suit second, or suit first and rank second; NDos::set_multiplex enables to do this conveniently.
NDos::set_multiplex does this by inheriting multiple std::sets, one storing the elements, and the others storing the iterator to the elements.
NDos::IterComp is a Callable type that compares the elements refered by two iterators.
Here is the code:
/*...*/
namespace NDos {
template <class T, class Comp0, class... Comps> class set_multiplex :
private std::set<T, Comp0>,
private std::set<
typename std::set<T, Comp0>::iterator,
IterComp<typename std::set<T, Comp0>::iterator, Comps>
>... {
private:
typedef std::set<T, Comp0> Base0;
public:
/*...*/
using typename Base0::iterator;
using typename Base0::const_iterator;
using typename Base0::reverse_iterator;
using typename Base0::const_reverse_iterator;
#define Bases std::set<iterator, IterComp<iterator, Comps>>
/*constructors*/
// copy constructor : default
// move constructor : default
// copy assignment operator : default
// move assignment operator : default
// destructor : default
/*...*/
void clear() noexcept {
Base0::clear();
Bases::clear()...;
}
iterator insert(const T &value) {
return emplace(value);
}
iterator insert(T &&value) {
return emplace(std::move(value));
}
iterator insert(const_iterator pos, const T &value) {
return emplace_hint(pos, value);
}
iterator insert(const_iterator pos, T &&value) {
return emplace_hint(pos, std::move(value));
}
template <class InputIt> void insert(InputIt first, InputIt last) {
while (first != last)
insert(*first++);
}
void insert(std::initializer_list<T> ilist) {
insert(std::make_move_iterator(ilist.begin()), std::make_move_iterator(ilist.end()));
}
template <class... Args> iterator emplace(Args &&...args) {
iterator i0 = Base0::emplace(std::forward<Args>(args)...).first;
Bases::insert(i0)...;
return i0;
}
template <class... Args> iterator emplace_hint(const_iterator pos, Args &&...args) {
iterator i0 = Base0::emplace_hint(pos, std::forward<Args>(args)...).first;
Bases::insert(i0)...;
return i0;
}
iterator erase(iterator pos) {
Bases::erase(pos)...;
return Base0::erase(pos);
}
iterator erase(const_iterator first, const_iterator last) {
while (first != last)
erase(first++);
}
size_type erase(const T &key) {
iterator pos = find(key);
if (pos == end())
return 0;
else {
erase(pos);
return 1;
}
}
void swap(set_multiplex &other) noexcept {
Base0::swap(other);
Bases::swap(other)...;
}
/*...*/
#undef Bases
};
}
The parameter packs aren't expanded properly. G++ 6.2 reports those errors each expansion: (In function clear, emplace, emplace_hint, erase, and swap)
error: expected ';' before '...' token
error: parameter packs not expanded with '...'
Why do these happen?
In C++11 you can't simply do this:
Bases::clear()...;
The same happens for all the other places where you have used ... in that way:
Bases::insert(i0)...;
Bases::erase(pos)...;
Bases::swap(other)...;
Try to use something like this:
void clear() noexcept {
Base0::clear();
int _[] = { 0, (Bases::clear(), 0)... };
(void)_; // silent warnings, nothing more
}
That is a common trick used around while waiting for C++17 and its fold expressions.
A particular mention for swap function: if you swap other with Base0, other theoretically will contain data in Base0 after the swap. Using it once more for another swap doesn't seem to be a good idea.
Maybe you should review the implementation of your swap function.

performance of emplace is worse than check followed by emplace

I have a std::unordered_map with a value_type that does not have a default constructor so I cannot do the following
auto k = get_key();
auto& v = my_map[k];
I ended up writing a helper function
value_type& get_value(key_type& key)
{
return std::get<0>(my_map.emplace(
std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(args_to_construct_value)
))->second;
}
but the performance was markedly worse (i.e. the value_type's constructor showed up in perf) than the following version.
value_type& get_value(key_type& key)
{
auto it = my_map.find(key);
if (it == my_map.end())
return std::get<0>(my_map.emplace(
std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(args_to_construct_value)
))->second;
else
return it->second;
}
I read from std::unordered_map::emplace object creation that emplace needs to construct the object in order to see if exists. But emplace is checking to see if this key value pair exists in the map before it returns.
Am I using emplace the wrong way? Is there a better pattern I should follow that:
Won't construct my value_type every lookup (as in my first method)
Won't do the check for to see if value_type exists in my map twice (as in my second method)
Thanks
Your code is unfortunately optimal for the standard library as it currently is.
The problem is that the emplace operation is designed to avoid copying, not to avoid unnecessary construction of the mapped type. In practical terms, what happens is that the implementation allocates and constructs a node, containing the map value_type i.e. pair<const Key, T>, and then hashes the key to determine whether the constructed node can be linked into the container; if this collides then the node is deleted.
As long as hash and equal_to are not too expensive, your code shouldn't do too much extra work.
An alternative is to use a custom allocator that intercepts 0-argument construction of your mapped type; the problem is that detecting such construction is pretty fiddly:
#include <unordered_map>
#include <iostream>
using Key = int;
struct D {
D() = delete;
D(D const&) = delete;
D(D&&) = delete;
D(std::string x, int y) { std::cout << "D(" << x << ", " << y << ")\n"; }
};
template<typename T>
struct A {
using value_type = T;
using pointer = T*;
using const_pointer = T const*;
using reference = T&;
using const_reference = T const&;
template<typename U> struct rebind { using other = A<U>; };
value_type* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
void deallocate(T* c, std::size_t n) { std::allocator<T>().deallocate(c, n); }
template<class C, class...Args> void construct(C* c, Args&&... args) { std::allocator<T>().construct(c, std::forward<Args>(args)...); }
template<class C> void destroy(C* c) { std::allocator<T>().destroy(c); }
std::string x; int y;
A(std::string x, int y): x(std::move(x)), y(y) {}
template<typename U> A(A<U> const& other): x(other.x), y(other.y) {}
template<class C, class...A> void construct(C* c, std::piecewise_construct_t pc, std::tuple<A...> a, std::tuple<>) {
::new((void*)c)C(pc, a, std::tie(x, y)); }
};
int main() {
using UM = std::unordered_map<Key, D, std::hash<Key>, std::equal_to<Key>, A<std::pair<const Key, D>>>;
UM um(0, UM::hasher(), UM::key_equal(), UM::allocator_type("hello", 42));
um[5];
}
You could use boost::optional<T> in order to be able to default construct the mapped type and then assign an initialized T to it later.
#include <cassert>
#include <unordered_map>
#include <boost/optional.hpp>
struct MappedType
{
explicit MappedType(int) {}
};
int main()
{
std::unordered_map<int, boost::optional<MappedType>> map;
boost::optional<MappedType>& opt = map[0];
assert(!opt.is_initialized());
opt = MappedType(2);
assert(opt.is_initialized());
MappedType& v = opt.get();
}
James, you've mostly answered your own question.
You're doing nothing wrong in either implementation. emplace simply does more work than find, especially when the key already exists in your unordered_map.
If your get_value helper function mostly receives duplicates, then calling emplace every time will cause a performance hot spot as you've observed.

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.