c++std-rangesstdtuplec++23structured-bindings

no match for operator * using min_element


Why can't I use operator * ? errors generated by gcc 14.

#include <algorithm>
#include <ranges>
using namespace std;

int main() {
    auto rng = views::iota(0, 3);
    const auto [a, b] = * ranges::min_element(views::cartesian_product(rng, rng));
    return 0;
}
prog.cc: In function 'int main()':
prog.cc:7:29: error: no match for 'operator*' (operand type is 'std::ranges::borrowed_iterator_t<std::ranges::cartesian_product_view<std::ranges::iota_view<int, int>, std::ranges::iota_view<int, int> > >')
    7 |         const auto [a, b] = * ranges::min_element(views::cartesian_product(rng, rng));
      |   

Solution

  • std::ranges::min_element "borrows" an iterator from the given range. In case of an rvalue std::cartesian_product (and any similar view), this would dangle in most circumstances since the lifetime of the std::cartesian_product would end after the iterator would be returned, so std::ranges::dangling is returned. See this related question: Why do std range algorithms return std::ranges::dangling for rvalue arguments instead of... well, just working?

    This would be safe in this case since the elements would be copied from before the cartesian_product rvalue's lifetime ends, but it prevents accidentally writing something like this:

    auto it = std::ranges::min_element(std::views::cartesian_product(rng, rng));
    const auto [a, b] = *it;  // `it` is dangling, would be UB
    

    The fix is to make it an lvalue:

    auto c = std::views::cartesian_product(rng, rng);
    const auto [a, b] = *std::ranges::min_element(c);