acraig5075
acraig5075

Reputation: 10756

Why can I not std::partition this std::unordered_map?

This doesn't build and I don't understand the compilation error.

#include <unordered_map>
#include <algorithm>

int main()
{
    std::unordered_map<int, size_t> occurences = { { 10, 2 }, { 20, 5 }, { 30, 0 }, { 40, 5 }, { 50, 0 }, { 100, 9 } };

    auto newEnd = std::partition(occurences.begin(), occurences.end(), [](const std::pair<int, size_t> &p)
        {
        return p.second == 0;
        });

    return 0;
}

g++ complains as follows. VS2013 is even more cryptic.

/usr/local/include/c++/6.3.0/bits/stl_pair.h: In instantiation of 'void std::pair<_T1, _T2>::swap(std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = long unsigned int]': /usr/local/include/c++/6.3.0/bits/stl_pair.h:473:7: required from 'void std::swap(std::pair<_T1, _T2>&, std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = long unsigned int]' /usr/local/include/c++/6.3.0/bits/stl_algobase.h:148:11: required from 'void std::iter_swap(_ForwardIterator1, _ForwardIterator2) [with _ForwardIterator1 = std::__detail::_Node_iterator, false, false>; _ForwardIterator2 = std::__detail::_Node_iterator, false, false>]' /usr/local/include/c++/6.3.0/bits/stl_algo.h:1500:20: required from '_ForwardIterator std::__partition(_ForwardIterator, _ForwardIterator, _Predicate, std::forward_iterator_tag) [with _ForwardIterator = std::__detail::_Node_iterator, false, false>; _Predicate = main()::&)>]' /usr/local/include/c++/6.3.0/bits/stl_algo.h:4524:30: required from '_BIter std::partition(_BIter, _BIter, _Predicate) [with _BIter = std::__detail::_Node_iterator, false, false>; _Predicate = main()::&)>]' main.cpp:12:4: required from here /usr/local/include/c++/6.3.0/bits/stl_pair.h:416:6: error: no matching function for call to 'swap(const int&, const int&)' swap(first, __p.first);

See it live on Coliru here

As far as I can tell this map meets the std::partition type requirements listed on cppreference.com so I'm stumped. My question is why doesn't it build?

Upvotes: -1

Views: 1401

Answers (3)

alfC
alfC

Reputation: 16270

All other answers are correct. unordered_map doesn't let you rearrange its elements in the same way that map doesn't and that's it. However there is a deeper conceptual problem.

When you created the unordered_map you decided that the order (any order) wouldn't matter, and suddenly you want the order to matter (partition operation). If you want a range, this requires your elements in a different container for sure, one in which the partition order matters.

You can always convert the partition into a comparison criterion, in which case you can use a multiset to store a new version of your collection.

#include<algorithm>
#include<unordered_map>
#include<set>

using std::cout; using std::cerr;

int main(){

    std::unordered_map<int, size_t> occurences = { { 10, 2 }, { 20, 5 }, { 30, 0 }, { 40, 5 }, { 50, 0 }, { 100, 9 } };

    auto null_second_pred = [](auto& e){return e.second == 0;};
    auto null_second_comp = [&](auto& a, auto& b){return null_second_pred(a) and (not null_second_pred(b));};
    std::multiset<std::pair<int, size_t>, decltype(null_second_comp)> 
        occurences2(occurences.begin(), occurences.end(), null_second_comp);

    assert( std::is_partitioned(occurences2.begin(), occurences2.end(), null_second_pred) );

}

(More conceptually you can move the elements into the new container but it won't make a difference in this case. Also you can't use multimap because you would loose information and the ordering criterion can't be on the second part of the pair.)

Upvotes: 1

Ivaylo Strandjev
Ivaylo Strandjev

Reputation: 71009

std::partition reorders the elements in the provided container, however you can not reorder the elements of a std::map - it has a predefined fixed order of its elements. The standard guarantees that when iterating over the elements of a map, you will always iterate them in increasing order.

As you mention unordered_map in the title I will also mention that unlike map it does not give guarantees about the order of its elements but reordering its elements is also not possible. After all unordered_map is unordered so it will never give any guarantee about the order in which you iterate over its elements.

Upvotes: 5

Jonathan Wakely
Jonathan Wakely

Reputation: 171393

The error is because the elements of a map and unordered_map are std::pair<const Key, value> not std::pair<Key, Value>, so you can't re-order them using an algorithm like std::partition because the const Key can't be modified:

error: no matching function for call to 'swap(const int&, const int&)'

Only the map itself can re-order the elements, and it keeps them in whatever order it needs to in order to maintain its invariants. If you re-order them you will corrupt the map's internal data structures.

Upvotes: 7

Related Questions