Search code examples
c++c++17argument-unpacking

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(*)

Solution

  • 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;