Skip to main content
Code fence
Source Link
Toby Speight
  • 81.7k
  • 14
  • 101
  • 308
    while (first < last)
    {
        std::pop_heap(first, last--, comp);
        auto y = *last;
        if (first < last) {
  • If you know the distance from first to last is at least \$2\$, you don't need the inner check. For range distances of \$2\$ or greater, you always pop twice and maybe push. When the distance is less than \$2\$, either the final stone exists at first (first < last) or is \$0\$ (first == last).
                *last = y - x;
  • y - x works fine if the comparator is std::less<>. What happens when the comparator switches to std::greater<>? The ordering changes. y would be the smaller value, so y-x would produce a negative value for signed numeric types and underflow for unsigned numeric types. If you want to support comparators, you should provide a difference function to ensure the ordering is applied correctly.
    template<class Iterator>
    typename Iterator::value_type stone_smash_destructive(Iterator first, Iterator last)
    {
        std::make_heap(first, last);
        while (last - first > 1)
        {
            std::pop_heap(first, last--);
            auto first_popped = *last;
            std::pop_heap(first, last--);
            auto second_popped = *last;
            if (second_popped < first_popped)
            {
                *last = first_popped - second_popped;
                std::push_heap(first, ++last);
            }
        }

        if (first == last) {
            return {0};
        }
        return *first;
    }
```
    while (first < last)
    {
        std::pop_heap(first, last--, comp);
        auto y = *last;
        if (first < last) {
  • If you know the distance from first to last is at least \$2\$, you don't need the inner check. For range distances of \$2\$ or greater, you always pop twice and maybe push. When the distance is less than \$2\$, either the final stone exists at first (first < last) or is \$0\$ (first == last).
                *last = y - x;
  • y - x works fine if the comparator is std::less<>. What happens when the comparator switches to std::greater<>? The ordering changes. y would be the smaller value, so y-x would produce a negative value for signed numeric types and underflow for unsigned numeric types. If you want to support comparators, you should provide a difference function to ensure the ordering is applied correctly.
    template<class Iterator>
    typename Iterator::value_type stone_smash_destructive(Iterator first, Iterator last)
    {
        std::make_heap(first, last);
        while (last - first > 1)
        {
            std::pop_heap(first, last--);
            auto first_popped = *last;
            std::pop_heap(first, last--);
            auto second_popped = *last;
            if (second_popped < first_popped)
            {
                *last = first_popped - second_popped;
                std::push_heap(first, ++last);
            }
        }

        if (first == last) {
            return {0};
        }
        return *first;
    }
```
    while (first < last)
    {
        std::pop_heap(first, last--, comp);
        auto y = *last;
        if (first < last) {
  • If you know the distance from first to last is at least \$2\$, you don't need the inner check. For range distances of \$2\$ or greater, you always pop twice and maybe push. When the distance is less than \$2\$, either the final stone exists at first (first < last) or is \$0\$ (first == last).
                *last = y - x;
  • y - x works fine if the comparator is std::less<>. What happens when the comparator switches to std::greater<>? The ordering changes. y would be the smaller value, so y-x would produce a negative value for signed numeric types and underflow for unsigned numeric types. If you want to support comparators, you should provide a difference function to ensure the ordering is applied correctly.
    template<class Iterator>
    typename Iterator::value_type stone_smash_destructive(Iterator first, Iterator last)
    {
        std::make_heap(first, last);
        while (last - first > 1)
        {
            std::pop_heap(first, last--);
            auto first_popped = *last;
            std::pop_heap(first, last--);
            auto second_popped = *last;
            if (second_popped < first_popped)
            {
                *last = first_popped - second_popped;
                std::push_heap(first, ++last);
            }
        }

        if (first == last) {
            return {0};
        }
        return *first;
    }
Source Link
Snowhawk
  • 6.3k
  • 1
  • 18
  • 34

    while (first < last)
    {
        std::pop_heap(first, last--, comp);
        auto y = *last;
        if (first < last) {
  • If you know the distance from first to last is at least \$2\$, you don't need the inner check. For range distances of \$2\$ or greater, you always pop twice and maybe push. When the distance is less than \$2\$, either the final stone exists at first (first < last) or is \$0\$ (first == last).
                *last = y - x;
  • y - x works fine if the comparator is std::less<>. What happens when the comparator switches to std::greater<>? The ordering changes. y would be the smaller value, so y-x would produce a negative value for signed numeric types and underflow for unsigned numeric types. If you want to support comparators, you should provide a difference function to ensure the ordering is applied correctly.
    template<class Iterator>
    typename Iterator::value_type stone_smash_destructive(Iterator first, Iterator last)
    {
        std::make_heap(first, last);
        while (last - first > 1)
        {
            std::pop_heap(first, last--);
            auto first_popped = *last;
            std::pop_heap(first, last--);
            auto second_popped = *last;
            if (second_popped < first_popped)
            {
                *last = first_popped - second_popped;
                std::push_heap(first, ++last);
            }
        }

        if (first == last) {
            return {0};
        }
        return *first;
    }
```