Namespaces
Variants

std::adjacent_difference

来自 zh.cppreference.net
 
 
算法库
受约束算法与范围算法 (C++20)
受约束算法,例如 ranges::copy、ranges::sort,...
排序与相关操作
划分操作
(C++11)    

排序操作
二分查找操作
(在已划分范围上)
集合操作(在有序范围上)
合并操作(在有序范围上)
堆操作
最小/最大操作
(C++11)
(C++17)
字典序比较操作
排列操作


 
 
定义于头文件 <numeric>
template< class InputIt, class OutputIt >
OutputIt adjacent_difference( InputIt first, InputIt last,
                              OutputIt d_first );
(1) (C++20 起为 constexpr)
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) (C++20 起为 constexpr)
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) 的值类型。

1) 若 [first, last) 为空,则不执行任何操作。
否则,按顺序执行以下操作:
  1. 创建类型为 acc 的累加器 T,并将其初始化为 *first。
  2. 将 acc 赋值给 *d_first。
  3. 对于 iter 中按顺序的每个迭代器 [++first, last),按顺序执行以下操作:
a) 创建类型为 val 的对象 T,并将其初始化为 *iter。
b) 计算 val - acc(C++20 前)val - std::move(acc)(C++20 起)。
c) 将结果赋值给 *++d_first。
d) 复制(C++20 前)移动(C++20 起) 赋值从 val 到 acc。
2) 若 [first, last) 为空,则不执行任何操作。
否则,按顺序执行以下操作:
  1. 将 *first 赋值给 *d_first。
  2. 对于 i 中的每个整数 [1, std::distance(first, last)),按顺序执行以下操作:
a) 计算 curr - prev,其中 curr 是 i的第 个下一个迭代器,first,而 prev 是 i - 1的第 个下一个迭代器first。
b) 将结果赋值给 *dest,其中 dest 是 i的第 个下一个迭代器d_first。
3) 与 (1) 相同,但改为计算 op(val, acc)(C++20 前)op(val, std::move(acc))(C++20 起)。
4) 与 (2) 相同,但改为计算 op(curr, prev)。

给定 binary_op 作为实际的二元运算:

  • 若满足以下任一条件,则程序非良构:
  • 对于重载 (1,3):
  • T 不可从 *first 构造。
  • acc 不可写入到 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] 中的任何迭代器或子范围失效。

参数

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 的要求。
-
OutputIt 必须满足 LegacyOutputIterator 的要求。
-
ForwardIt1, ForwardIt2 必须满足 LegacyForwardIterator 的要求。

返回值

指向最后被写入元素之后位置的迭代器,若 [ first , last ) 为空范围则返回 d_first 。

复杂度

给定 N 为 std:: distance ( first, last ) :

1,2) 恰好需要 N-1 次 operator - 运算符的应用。
3,4) 恰好执行 N-1 次二元函数 op 的调用。

异常

带有名为 ExecutionPolicy 模板参数的重载按以下方式报告错误:

  • 如果作为算法一部分调用的函数执行抛出异常,且 ExecutionPolicy 是某个 标准策略 ,则调用 std::terminate 。对于其他任何 ExecutionPolicy ,其行为由实现定义。
  • 如果算法无法分配内存,则抛出 std::bad_alloc 。

可能的实现

相邻差分 (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;
}
相邻差分 (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];
// 正确:在需要时执行类型转换
// 1. 创建 char 类型的“acc”(值类型)
// 2. 将“acc”赋值给“o_array”的第一个元素
// 3. char 参数用于 long 类型乘法运算(char → long)
// 4. long 类型的乘积被赋值给输出范围(long → int)
// 5. 将“i_array”的下一个值赋值给“acc”
// 6. 返回步骤3处理输入范围内的剩余元素
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()
{
    // 默认实现 - 计算相邻元素的差值
    std::vector v{4, 6, 9, 13, 18, 19, 19, 15, 10};
    println("初始状态, v = ", v);
    std::adjacent_difference(v.begin(), v.end(), v.begin());
    println("修改后 v = ", v);
    // 斐波那契数列生成
    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("斐波那契数列, a = ", a);
}

输出:

初始状态, v = 4 6 9 13 18 19 19 15 10 
修改后 v = 4 2 3 4 5 1 0 -4 -5 
斐波那契数列, a = 1 1 2 3 5 8 13 21 34 55

缺陷报告

下列行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

缺陷报告 适用标准 发布行为 正确行为
LWG 242 C++98 op 不允许有副作用 不能修改
所涉及的区间
LWG 539 C++98 缺少确保结果求值
和赋值有效的类型要求
已添加
LWG 3058 C++17 对于重载 (2,4) ,每次调用
operator - 或 op 的结果被赋值给临时
对象,再将该对象赋值给输出区间
直接将结果
赋值给输出
区间

参见

计算范围内元素的部分和
(函数模板)
对范围内元素进行求和或折叠操作
(函数模板)