Search code examples
c++algorithmtuplesstd-rangesc++23

Is there a way to get projection to zipped vectors std::tuple without lambda?


As shown in the code below I can sort zipped vectors with std::ranges::sort, using lambda to specify that sorting should be done by the first vector. Is there a way to use projection, specifying the tuple field name (or something else) like shown in the examples (2) and (3)?

The example (2) worked in ranges3 where the element was implemented as std::pair (or at least it was possible to access it this way). Now with standard library ranges, it is implemented with tuples and this approach doesn’t work.

Can I get this done without lambda here?

Why do I care? I need to keep separate and available comparator and projection to use with my tuned versions. Additional benefit is less code, easier to write, read and support.

#include <algorithm>
#include <ranges>
#include <vector>

int main()
{
    std::vector<float> x_values = { 3.0f, 1.0f, 2.0f };
    std::vector<float> y_values = { 5.0f, 6.0f, 7.0f };

    // (1) Compiles
    std::ranges::sort(std::ranges::views::zip(x_values, y_values), [](const auto& lhs, const auto& rhs) {
        return std::get<0>(lhs) < std::get<0>(rhs);
    });

    // (2) Doesn't compile since in ranges they use tuples
    std::ranges::sort(std::ranges::views::zip(x_values, y_values), std::less<>{}, &std::pair<int, int>::first);

    // (3) I guess doesn't compile since get is a member function template, not a member field
    std::ranges::sort(std::ranges::views::zip(x_values, y_values), std::less<>{}, &std::get<0>);
}

Here is the demo.

(Too verbose logs have been removed from the question, since we already have the answers and the logs are not needed. Let me know if you want me to get them back).


Solution

  • I need to keep separate and available comparator and projection to use with my tuned versions. Additional benefit is less code, easier to write, read and support.

    What I can think of is, old-school functors, which is customized something like:

    template<std::size_t N> struct Get
    {
        template<typename T>
        constexpr auto operator()(const T& tuple) const 
        {
            return std::get<N>(tuple);
        }
    };
    

    Now you can write:

    auto zipped = std::views::zip(x_values, y_values);
    
    // Sort by first element (x_values)
    std::ranges::sort(zipped, std::less{}, Get<0>{});
    
    // Or ...sort by second element (y_values)
    std::ranges::sort(zipped, std::less{}, Get<1>{});
    
    // so on...
    

    See a live demo