std::adjacent_difference
| Определено в заголовке <numeric>
|
||
template< class InputIt, class OutputIt >
OutputIt adjacent_difference( InputIt first, InputIt last,
OutputIt d_first );
|
(1) | (constexpr начиная с C++20) |
template< class ExecutionPolicy,
class ForwardIt1, class ForwardIt2 >
ForwardIt2 adjacent_difference( ExecutionPolicy&& policy,
ForwardIt1 first, ForwardIt1 last,
ForwardIt2 d_first );
|
(2) | (начиная с C++17) |
template< class InputIt, class OutputIt, class BinaryOp >
OutputIt adjacent_difference( InputIt first, InputIt last,
OutputIt d_first, BinaryOp op );
|
(3) | (constexpr начиная с C++20) |
template< class ExecutionPolicy,
class ForwardIt1, class ForwardIt2, class BinaryOp >
ForwardIt2 adjacent_difference( ExecutionPolicy&& policy,
ForwardIt1 first, ForwardIt1 last,
ForwardIt2 d_first, BinaryOp op );
|
(4) | (начиная с C++17) |
Пусть T будет типом значений decltype(first).
[first, last) пуст, ничего не делает.- Создаёт аккумулятор
accтипаTи инициализирует его значением*first. - Присваивает
accв*d_first. - Для каждого итератора
iterв[++first,last)по порядку выполняет следующие операции:
val типа T и инициализирует его значением *iter.val - acc(до C++20)val - std::move(acc)(начиная с C++20).*++d_first.val в acc.[first, last) пуст, ничего не делает.- Присваивает
*firstв*d_first. - Для каждого целого
iв[1,std::distance(first, last))выполняет следующие операции по порядку:
curr - prev, где curr — следующий i-й итератор от first, а prev — следующий i - 1-й итератор от first.*dest, где dest — следующий i-й итератор от d_first.op(val, acc)(до C++20)op(val, std::move(acc))(начиная с C++20) вместо этого.op(curr, prev) вместо этого.Пусть binary_op — фактическая бинарная операция:
- Если выполняется любое из следующих условий, программа считается некорректной:
- Для перегрузок (1,3):
Tне конструируется из*first.accне является writable вd_first.- Результат
binary_op(val, acc)(до C++20)binary_op(val, std::move(acc))(начиная с C++20) не является записываемым вd_first.
- Для перегрузок (2,4):
*firstне является записываемым вd_first.- Результат
binary_op(*first, *first)не является записываемым вd_first.
- Пусть
d_last— итератор, который должен быть возвращён. Если выполняется любое из следующих условий, поведение не определено:
|
(начиная с C++20) |
- Для перегрузок (2,4),
[first,last)и[d_first,d_last)перекрываются. binary_opизменяет любой элемент[first,last)или[d_first,d_last).binary_opделает недействительным любой итератор или поддиапазон в[first,last]или[d_first,d_last].
- Для перегрузок (2,4),
Параметры
| first, last | - | пара итераторов, определяющая диапазон элементов для |
| d_first | - | начало целевого диапазона |
| policy | - | политика выполнения, которую следует использовать |
| op | - | бинарный функциональный объект операции, который будет применяться. Сигнатура функции должна быть эквивалентна следующей:
Сигнатура не обязана содержать |
| Требования к типам | ||
-InputIt должен соответствовать требованиям LegacyInputIterator.
| ||
-OutputIt должен соответствовать требованиям LegacyOutputIterator.
| ||
-ForwardIt1, ForwardIt2 должен соответствовать требованиям LegacyForwardIterator.
| ||
Возвращаемое значение
Итератор на элемент, следующий за последним записанным элементом, или d_first, если [first, last) пуст.
Сложность
Пусть N равно std::distance(first, last):
operator-.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++20
acc = std::move(val);
}
return ++d_first;
}
|
| adjacent_difference (3) |
template<class InputIt, class OutputIt, class BinaryOp>
constexpr // since C++20
OutputIt adjacent_difference(InputIt first, InputIt last,
OutputIt d_first, BinaryOp 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++20
acc = std::move(val);
}
return ++d_first;
}
|
Примечания
acc был введён в результате решения LWG issue 539. Причина использования acc вместо непосредственного вычисления разностей заключается в том, что семантика последнего сбивает с толку, если следующие типы не совпадают:
- тип значений
InputIt - записываемый тип(ы)
OutputIt - типы параметров
operator-илиop - возвращаемый тип
operator-илиop
acc служит промежуточным объектом для кэширования значений итерируемых элементов:
- его тип — это тип значений
InputIt - значение, записываемое в
d_first(которое является возвращаемым значениемoperator-илиop), присваивается ему - его значение передаётся в
operator-или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>
void println(auto comment, const auto& sequence)
{
std::cout << comment;
for (const auto& n : sequence)
std::cout << n << ' ';
std::cout << '\n';
};
int main()
{
// Default implementation - the difference between two adjacent items
std::vector v{4, 6, 9, 13, 18, 19, 19, 15, 10};
println("Initially, v = ", v);
std::adjacent_difference(v.begin(), v.end(), v.begin());
println("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<>{});
println("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 3058 | C++17 | для перегрузок (2,4) результат каждого вызоваoperator- или op присваивался временномуобъекту, и этот объект присваивался выходному диапазону |
присваивать результаты непосредственно в выходной диапазон |
Смотрите также
| вычисляет частичную сумму диапазона элементов (шаблон функции) | |
| суммирует или свёртывает диапазон элементов (шаблон функции) |