Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto-unpacking a pair of iterators

In C++, if a function returns a std::pair<int, int>, we can auto-receive it as follows:

auto pr = some_function();
std::cout << pr.first << ' ' << pr.second;

Now, C++17 standard provides a beautiful way of unpacking this pair directly into separate variables as follows:

auto [x, y] = some_function();
std::cout << x << ' ' << y;

And then there is std::minmax_element() library function, which returns a pair of iterators. So if I pass a vector<int> to this function, it gives me back pair of iterators pointing to the smallest and the largest element in the vector.

Now one way I can accept these iterators is as usual, and then de-reference them later as follows.

std::vector<int> v = {4,1,3,2,5};
auto [x, y] = std::minmax_element(v.begin(), v.end());
std::cout << (*x) << ' ' << (*y);   // notice the asterisk(*)

Now my question is: Is there any way to de-reference them while unpacking? Or more precisely, given the following code, can I replace var1 and var2 with something that is valid C++ and prints the value pointed by those iterators?

std::vector<int> v = {4,1,3,2,5};
auto [var1, var2] = std::minmax_element(v.begin(), v.end());
std::cout << var1 << ' ' << var2;   // notice there is NO asterisk(*)
like image 791
MrProgrammer Avatar asked May 07 '20 15:05

MrProgrammer


1 Answers

Sure. Write a function that takes a pair of dereferencable things and returns the result of dereferencing them:

template <typename Iterator,
    typename R = typename std::iterator_traits<Iterator>::reference>
auto deref(std::pair<Iterator, Iterator> p)
    -> std::pair<R, R>
{
    return {*p.first, *p.second};
}

And then use that function:

auto [var1, var2] = deref(std::minmax_element(v.begin(), v.end()));

Noting that this is UB if the range is empty because you're dereferencing the end iterator, twice.


Or, to be nicer:

struct deref_t {
    template <typename It,
        typename R = typename std::iterator_traits<Iterator>::reference>
    friend auto operator|(std::pair<It, It> p, deref_t)
        -> std::pair<R, R>
    {
        return { *p.first, *p.second };
    }
};
inline constexpr deref_t deref{};

Which allows:

auto [var1, var2] = std::minmax_element(v.begin(), v.end()) | deref;
like image 104
Barry Avatar answered Nov 07 '22 03:11

Barry