Путаница с std :: уникальной реализацией? - PullRequest
0 голосов
/ 28 июня 2018

Согласно этой статье , одной из возможных реализаций std::unique является

template<class ForwardIt>
ForwardIt unique(ForwardIt first, ForwardIt last)
{
    if (first == last)
        return last;

    ForwardIt result = first;
    while (++first != last) {
        if (!(*result == *first) && ++result != first) {
            *result = std::move(*first);
        }
    }
    return ++result;
}

Тем не менее, я не понимаю, для чего итератор сравнения? Почему if (!(*result == *first) && ++result != first), а не просто if (!(*result++ == *first))? Какова цель сравнения двух итераторов?

Ответы [ 3 ]

0 голосов
/ 28 июня 2018

Давайте перепишем код на более мелкие шаги (код эквивалентен тому, который указан в вопросе - я только что разделил оператор if на две отдельные части):

template<class ForwardIt>
ForwardIt unique(ForwardIt first, ForwardIt last)
{
    // are there elements to test?
    if (first == last)
        return last;

    // there are elements so point result to the first one
    ForwardIt result = first;

    // then increment first and check if we are done
    while (++first != last) {

        // if the value of first is still the same as the value of result
        // then restart the loop (incrementing first and checking if we are done)
        // Notice that result isn't moved until the values differ
        if (*result == *first)
            continue;

        // increment result and move the value of first to this new spot
        // as long as they don't point to the same place
        // So result is only moved when first points to a new (different) value 
        if (++result != first) {
            *result = std::move(*first);
        }
    }

    // return one past the end of the new (possibly shorter) range.
    return ++result;
}

Вот пример:

result
   v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  2  |  3  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
   ^                                               ^
 first                                           last

Шаг 1 - увеличить сначала и сравнить значение first со значением результата:

result
   v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  2  |  3  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
         ^                                         ^
       first                                      last

Шаг 2 - значения различаются, поэтому результат увеличивается, но теперь они указывают на одно и то же место, поэтому перемещение излишне, а мы этого не делаем

      result
         v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  2  |  3  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
         ^                                         ^
       first                                      last

Шаг 3 - увеличить сначала и сравнить значение first со значением результата:

      result
         v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  2  |  3  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
               ^                                   ^
             first                                last

Шаг 4 - значения одинаковы, поэтому перезапустите цикл (увеличивая сначала и сравнивая значение first со значением результата):

      result
         v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  2  |  3  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
                     ^                             ^
                   first                          last

Шаг 5 - значения отличаются друг от друга, поэтому результат увеличивается, они указывают на разные места, поэтому значение first переводится в значение результата:

            result
               v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  3  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
                     ^                             ^
                   first                          last

Шаг 6 - увеличить сначала и сравнить значение first со значением результата:

            result
               v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  3  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
                           ^                       ^
                         first                    last

Шаг 7 - значения отличаются друг от друга, поэтому результат увеличивается, они указывают на разные места, поэтому значение first переводится в значение результата:

                  result
                     v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  4  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
                           ^                       ^
                         first                    last

Шаг 8 - увеличить сначала и сравнить значение first со значением результата:

                  result
                     v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  4  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
                                 ^                 ^
                               first              last

Шаг 9 - значения одинаковы, поэтому перезапустите цикл (увеличивая сначала и сравнивая значение first со значением результата):

                  result
                     v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  4  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
                                       ^           ^
                                     first        last

Шаг 10 - значения одинаковы, поэтому перезапустите цикл (увеличивая сначала и сравнивая значение first со значением результата):

                  result
                     v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  4  |  4  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
                                             ^     ^
                                           first  last

Шаг 11 - значения отличаются друг от друга, поэтому результат увеличивается, они указывают на разные места, поэтому значение first переводится в значение результата:

                        result
                           v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  4  |  5  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
                                             ^      ^
                                           first   last

Шаг 12 - сначала увеличивается, а цикл while заканчивается, потому что первая и последняя указывают на одно и то же место, а затем после результата увеличения цикла, чтобы он стал новым конечным итератором для уникального диапазона:

                              result
                                 v
+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  2  |  3  |  4  |  5  |  4  |  4  |  5  |
+-----+-----+-----+-----+-----+-----+-----+-----+
                                                    ^
                                                last&first
0 голосов
/ 28 июня 2018
ForwardIt result = first;
while (++first != last) {
    if (!(*result == *first) && ++result != first) {
        *result = std::move(*first);
    }
}
return ++result;

можно переписать как

ForwardIt result = first;

// result is the last element different from previous values
// all the equal elements after result=first are useless
// *result = *first
// first is the last element examined

// determine largest range of useless elements

// *result = before(*first) 
// i.e. result has the value of former value (before call) of element *first (current value of first)
// so first is the last element on which we know something

extend_useless_range: 
    // so range ]result,first] is useless 
    first++;
    // now range ]result,first[ is useless 
    // and first is the first element yet to be examined

    if (first == last) {
         // ]result,last[ is useless 
         goto end_loop;
    }
    if (*result == *first) {
         // *first is useless
         // so range ]result,first] is useless 
         goto extend_useless_range;
    }
    // *first is useful

    // range ]result,first[ is biggest useless range after result
    result++; 
    // range [result,first[ is useless (and *first is useful)

    if (result != first) {
        // [result,first[ is nonempty
        *result = std::move(*first);
        // *result is useful and *first is useless (undetermined value)
        // ]result,first] is useless
    }
    else {
        // [result,first[ = ]result,first] = {} and is useless
    }
    // ]result,first] is useless
    goto extend_useless_range;

end_loop: // ]result,last[ is useless 
    result++; 
    // [result,last[ is useless 
    return result;
0 голосов
/ 28 июня 2018

Если вы делаете if(!(*result++ == *first)), вы всегда увеличиваете result в своем состоянии. Но если !(*result == *first) ложно, вторая часть условия никогда не будет оценена благодаря оценке короткого замыкания.

Разница имеет решающее значение для значения "уникальный".

...