I am using STL associative containers (std::set
and std::map
) with keys which hold a std::unique_ptr<>
instance. The key definition is equivalent to following:
struct Key
{
std::unique_ptr<Object> object;
bool operator== (const Key& rhs) const { return object->equal (*rhs.object); }
bool operator< (const Key& rhs) const { return object->less (*rhs.object); }
}
It is known that STL associative containers (esp. since C++11) do not have a way to obtain a non-const reference to the key to move from. And my keys are noncopyable, so c++: Remove element from container and get it back does not work.
Is there a non-UB way to overcome this problem?
My current solution is following:
template <typename T>
using map_pair_type = std::pair<typename T::key_type, typename T::mapped_type>;
template <typename T>
typename T::value_type take_set (T& container, typename T::iterator iterator)
{
typename T::value_type result = std::move (const_cast<typename T::value_type&> (*iterator));
container.erase (iterator);
return result;
}
template <typename T>
map_pair_type<T> take_map (T& container, typename T::iterator iterator)
{
map_pair_type<T> result {
std::move (const_cast<typename T::key_type&> (iterator->first)),
std::move (iterator->second)
};
container.erase (iterator);
return result;
}
This is one of these:
Really sorry. We tried to make this work and couldn't get it through committee.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3586.pdf
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3645.pdf
Your solution is as good as it gets as far as I know. Your map solution does exhibit undefined behavior. And it will go seriously bad if the second move throws an exception. Other than that, I suspect it will work. And I suspect I'll get down voted for saying that.
The reason for the UB is that the key is defined as const
(as opposed to just being referenced by a const reference). Casting away const
in that situation (and having a move constructor modify the object) is UB.
Had N3586 been accepted, you could just:
move_only_type mot = move(*s.remove(s.begin()));
or:
move_only_key mok = move(m.remove(m.begin())->first);
N3586/N3645 made a good showing in committee. It was discussed and made it through the working group stage, only to be shot down in full committee. The concern is that the std::lib would have to commit UB in order to implement it. It has not been resubmitted.
Update
One can now do this in C++17, but the member function is called extract
instead of remove
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With