c++sortingc++23

Why isn't std::partial_sort_copy populating the vector?


I'm trying to pick the first k elements from a std::map based on a custom compactor and insert them into a vector. I'm trying to do this via std::partial_sort_copy. However, when I run the code, I find that nothing was inserted in the vector. Here;s my code:

#include <algorithm>                                                                                                                                                                                           
#include <cstdint>
#include <iostream>
#include <map>
#include <vector>

struct Values
{
        uint32_t a = 0;
        uint32_t b = 0;

        Values(uint32_t x, uint32_t y): a(x), b(y) {}
};

template <typename MapType>
auto print_top_k(std::size_t k, MapType&& map)
{
        std::vector<std::pair<typename MapType::key_type, typename MapType::mapped_type>> v;
        v.reserve(map.size());
        std::cout << v.size() << "\n";

        // std::transform(map.begin(), map.end(), std::back_inserter(v), [](const auto& kv) { return kv; });
        // std::sort(v.begin(), v.end(), [](const auto& lhs, const auto& rhs){ return lhs.second.a > rhs.second.b; }); 
        // v.erase(v.begin() + k, v.end());

        std::partial_sort_copy(map.begin(), map.end(), v.begin(), std::next(v.begin(), k), [](const auto& lhs, const auto& rhs){ return lhs.second.a > rhs.second.b; });
        std::cout << v.size() << "\n";

        std::cout << std::distance(map.begin(), map.end()) << " " << std::distance(v.begin(), std::next(v.begin(), k)) << "\n";
        for(const auto& kv: v)
            std::cout << kv.first << " -> (" << kv.second.a << ", " << kv.second.b << ")\n";
}

int main()
{
        std::map<uint32_t, Values> data;
        for(uint32_t i = 0; i < 100; i++)
            data.emplace(std::piecewise_construct, std::forward_as_tuple(i), std::forward_as_tuple(2*i, 3*i));

        print_top_k(10, std::move(data));

        return 0;
}

Here's the link to compiler explorer.

Here's the output that I get when I run this code:

0
0
100 10

The commented out code performs the computation that I want to do using std::partial_sort_copy. What am I doing wrong? Why doesn't my implementation work?


Solution

  • Under the circumstances (you already know the size of the destination container), it's almost certainly easiest to set the correct size.

    You also need to be able to default-construction a Values object (well, strictly speaking, that's not an absolute necessity, but it's a common an effective way to handle the situation).

    #include <algorithm>                                                                                                                                                                                           
    #include <cstdint>
    #include <iostream>
    #include <map>
    #include <vector>
    
    struct Values
    {
            uint32_t a = 0;
            uint32_t b = 0;
    
            Values(uint32_t x, uint32_t y): a(x), b(y) {}
            Values() = default;
    };
    
    template <typename MapType>
    auto print_top_k(std::size_t k, MapType&& map)
    {
            std::vector<std::pair<typename MapType::key_type, typename MapType::mapped_type>> v{k};
            
            std::partial_sort_copy(map.begin(), map.end(), v.begin(), v.end(), [](const auto& lhs, const auto& rhs){ return lhs.second.a > rhs.second.b; });
    
            for(const auto& kv: v)
                std::cout << kv.first << " -> (" << kv.second.a << ", " << kv.second.b << ")\n";
    }
    
    int main()
    {
            std::map<uint32_t, Values> data;
            for(uint32_t i = 0; i < 100; i++)
                data.emplace(std::piecewise_construct, std::forward_as_tuple(i), std::forward_as_tuple(2*i, 3*i));
    
            print_top_k(10, std::move(data));
    
            return 0;
    }
    

    Result:

    99 -> (198, 297)
    98 -> (196, 294)
    86 -> (172, 258)
    97 -> (194, 291)
    62 -> (124, 186)
    61 -> (122, 183)
    43 -> (86, 129)
    63 -> (126, 189)
    85 -> (170, 255)
    45 -> (90, 135)
    

    Live on Godbolt