Spec-Zone.ru › C++

std::adjacent_difference

Определено в заголовке <numeric>
(1)
template< class InputIt, class OutputIt >
OutputIt adjacent_difference( InputIt first, InputIt last, OutputIt d_first );
(до C++20)
template< class InputIt, class OutputIt >
constexpr OutputIt adjacent_difference( InputIt first, InputIt last,
                                        OutputIt d_first );
(с C++20)
template< class ExecutionPolicy, class ForwardIt1, class ForwardIt2 >
ForwardIt2 adjacent_difference( ExecutionPolicy&& policy,
                                ForwardIt1 first, ForwardIt1 last,
                                ForwardIt2 d_first );
(2) (с C++17)
(3)
template< class InputIt, class OutputIt, class BinaryOperation >
OutputIt adjacent_difference( InputIt first, InputIt last, 
                              OutputIt d_first, BinaryOperation op );
(до C++20)
template< class InputIt, class OutputIt, class BinaryOperation >
constexpr OutputIt adjacent_difference( InputIt first, InputIt last, 
                                        OutputIt d_first,
                                        BinaryOperation op );
(с C++20)
template< class ExecutionPolicy,
          class ForwardIt1, class ForwardIt2, class BinaryOperation >
ForwardIt2 adjacent_difference( ExecutionPolicy&& policy,
                                ForwardIt1 first, ForwardIt1 last, 
                                ForwardIt2 d_first, BinaryOperation op );
(4) (с C++17)

Если [first, last) не пусто, вычисляет разницу между вторым и первым элементом каждой смежной пары элементов и записывает разницу в диапазон, начинающийся в d_first + 1. Неизменная копия *first записывается в *d_first. Перегрузки (1,2) используют оператор- для вычисления разностей, перегрузки (3,4) используют заданную бинарную функцию op, применяя std::move к своим операндам справа(с C++11).

Эквивалентная операция для перегрузки (1), если [first, last) не пусто, использует накопитель acc для хранения значения, которое нужно вычесть:

std::iterator_traits<InputIt>::value_type acc = *first;
*d_first = acc;
 
std::iterator_traits<InputIt>::value_type val1 = *(first + 1);
*(d_first + 1) = val1 - std::move(acc);
// or *(d_first + 1) = op(val1, std::move(acc)); for overload (2)
acc = std::move(val1);
 
std::iterator_traits<InputIt>::value_type val2 = *(first + 2);
*(d_first + 2) = val2 - std::move(acc);
acc = std::move(val2);
 
std::iterator_traits<InputIt>::value_type val3 = *(first + 3);
*(d_first + 3) = val3 - std::move(acc);
acc = std::move(val3);
// ...

Эквивалентная операция для перегрузки (3), если [first, last) не пусто:

// performed first
*d_first = *first;
 
// performed after the initial assignment, might not be sequenced
*(d_first + 1) = *(first + 1) - *(first);
// or *(d_first + 1) = op(*(first + 1), *(first)); for overload (4)
*(d_first + 2) = *(first + 2) - *(first + 1);
*(d_first + 3) = *(first + 3) - *(first + 2);
...

Если op делает недействительными какие-либо итераторы (включая любые конечные итераторы) или изменяет любые элементы вовлечённых диапазонов, поведение не определено.

Для перегрузок (1,3), если std::iterator_traits<InputIt>::value_type не является копируемым(до C++11)перемещаемым(с C++11), поведение не определено.

Параметры

first, last - диапазон элементов
d_first - начало целевого диапазона
policy - политика выполнения. См. политику выполнения для подробностей.
op - бинарный функциональный объект, который будет применяться.

Подпись функции должна быть эквивалентна следующей:

Ret fun(const Type1 &a, const Type2 &b);

Подпись не должна содержать const &.
Типы Type1 и Type2 должны быть такими, чтобы объект типа iterator_traits<InputIt>::value_type мог быть неявно преобразован в оба из них. Тип Ret должен быть таким, чтобы объект типа OutputIt мог быть обращён и присвоен значение типа Ret. ​

Требования к типу
-InputIt должно соответствовать требованиям LegacyInputIterator. Для перегрузок (1,3) его тип значения должен быть создаваемым из *first.
-OutputIt должно соответствовать требованиям LegacyOutputIterator. acc (определённый выше) и результат val - acc (для (1)) или op(val, acc) (для (3))(до C++11)val - std::move(acc) (для (1)) или op(val, std::move(acc)) (для (3))(с C++11) должны быть записываемыми в d_first.
-ForwardIt1, ForwardIt2 должно соответствовать требованиям LegacyForwardIterator. Результат *first и результат *first - *first (для (2)) или op(*first, *first) (для (4)) должны быть записываемыми в d_first.

Возвращаемое значение

Итератор на элемент после последнего записанного элемента, или d_first если [first, last) пусто.

Сложность

Учитывая N как std::distance(first, last) - 1:

1,2) ровно N применений operator-
3,4) ровно N применений бинарной функции op

Исключение

Перегрузки с шаблоным параметром с именем ExecutionPolicy сообщают об ошибках следующим образом:

  • Если выполнение функции, вызванной как часть алгоритма, вызывает исключение и ExecutionPolicy является одной из стандартных политик, вызывается std::terminate. Для любой другой ExecutionPolicy, поведение не определено.
  • Если алгоритм не может выделить память, выбрасывается std::bad_alloc.

Возможная реализация

adjacent_difference (1)
template<class InputIt, class OutputIt>
constexpr // since C++20
OutputIt adjacent_difference(InputIt first, InputIt last, OutputIt d_first)
{
    if (first == last)
        return d_first;
 
    typedef typename std::iterator_traits<InputIt>::value_type value_t;
    value_t acc = *first;
    *d_first = acc;
 
    while (++first != last)
    {
        value_t val = *first;
        *++d_first = val - std::move(acc); // std::move since C++11
        acc = std::move(val);
    }
 
    return ++d_first;
}
adjacent_difference (3)
template<class InputIt, class OutputIt, class BinaryOperation>
constexpr // since C++20
OutputIt adjacent_difference(InputIt first, InputIt last, 
                             OutputIt d_first, BinaryOperation op)
{
    if (first == last)
        return d_first;
 
    typedef typename std::iterator_traits<InputIt>::value_type value_t;
    value_t acc = *first;
    *d_first = acc;
 
    while (++first != last)
    {
        value_t val = *first;
        *++d_first = op(val, std::move(acc)); // std::move since C++11
        acc = std::move(val);
    }
 
    return ++d_first;
}

Примечания

acc была введена из-за разрешения LWG issue 539. Причина использования acc вместо непосредственного вычисления разностей заключается в том, что семантика последнего может быть неясной при несовпадении типов:

  • тип значения InputIt
  • тип(ы) записи OutputIt
  • типы параметров оператора- или op
  • тип возвращаемого значения оператора- или op

acc служит промежуточным объектом для кэширования значений итерируемых элементов:

  • его тип — тип значения InputIt
  • значение, записанное в d_first (которое является результатом оператора- или op) присваивается ему
  • его значение передаётся в оператор- или op
char i_array[4] = {100, 100, 100, 100};
int  o_array[4];
 
// OK: performs conversions when needed
// 1. creates `acc` of type char (the value type)
// 2. `acc` is assigned to the first element of `o_array`
// 3. the char arguments are used for long multiplication (char -> long)
// 4. the long product is assigned to the output range (long -> int)
// 5. the next value of `i_array` is assigned to `acc`
// 6. go back to step 3 to process the remaining elements in the input range
std::adjacent_difference(i_array, i_array + 4, o_array, std::multiplies<long>{});

Пример

#include <array>
#include <functional>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
 
auto print = [](auto comment, auto const& sequence)
{
    std::cout << comment;
    for (const auto& n : sequence)
        std::cout << n << ' ';
    std::cout << '\n';
};
 
int main()
{
    // Default implementation - the difference b/w two adjacent items
    std::vector v {4, 6, 9, 13, 18, 19, 19, 15, 10};
    print("Initially, v = ", v);
    std::adjacent_difference(v.begin(), v.end(), v.begin());
    print("Modified v = ", v);
 
    // Fibonacci
    std::array<int, 10> a {1};
    std::adjacent_difference(std::begin(a), std::prev(std::end(a)),
                             std::next(std::begin(a)), std::plus<>{});
    print("Fibonacci, a = ", a);
}

Вывод:

Initially, v = 4 6 9 13 18 19 19 15 10 
Modified v = 4 2 3 4 5 1 0 -4 -5 
Fibonacci, a = 1 1 2 3 5 8 13 21 34 55

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применён к Поведение, как опубликовано Правильное поведение
LWG 242 C++98 op не мог иметь побочных эффектов он не может изменять
вовлечённые диапазоны
LWG 539 C++98 требования к типу для результатов
вычислений и присваиваний отсутствовали
добавлены
LWG 2055
(P0616R0)
C++11 acc не перемещался во время накопления он перемещается
LWG 3058 C++17 для перегрузок (2,4) результат каждого вызова
оператора- или op присваивался временной
переменной, а эта переменная присваивалась выходному диапазону
присваивать результаты
непосредственно
выходному
диапазону

См. также

partial_sum
вычисляет частичную сумму диапазона элементов
(шаблон функции)
accumulate
суммирует или складывает по определённому правилу диапазон элементов
(шаблон функции)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/algorithm/adjacent_difference

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API