C++ <algorithm> 实现说明

C++ <algorithm> implementation explained

本文关键字:实现 说明 gt algorithm C++ lt      更新时间:2023-10-16

当我想知道C++标准库中的算法是如何实现的时,我总是查看http://en.cppreference.com/w/cpp/algorithm,这是一个很好的来源。但有时我不了解一些实现细节,我需要解释为什么某些事情是以这种特殊的方式完成的。例如,在std::copy_n的实现中,为什么第一个赋值是在循环之外进行的,因此循环从1开始?

template< class InputIt, class Size, class OutputIt>
OutputIt copy_n(InputIt first, Size count, OutputIt result)
{
    if (count > 0) {
        *result++ = *first;
        for (Size i = 1; i < count; ++i) {
            *result++ = *++first;
        }
    }
    return result;
}

另外:你知道一个解释可能的算法实现的网站吗?

将其与初始实现进行比较:

template< class InputIt, class Size, class OutputIt>
OutputIt copy_n(InputIt first, Size count, OutputIt result)
{
  for (Size i = 0; i < count; ++i) {
    *result++ = *first++;
  }
  return result;
}

此版本对first进行了一次增量!

  1. count==0都进行first0增量。

  2. count==1,他们的版本对first进行零增量。上面的版本做了1。

  3. count==2,他们的版本做一个增量的first。上面的版本做了2。

一种可能性是处理可取消引用但不可增加的迭代器。至少在STL时代,这是有区别的。我不确定输入迭代器今天是否具有此属性。

如果您使用naive实现,就会出现一个错误,下面是一些文档,声称"实际的读取操作是在迭代器递增时执行的,而不是在它被取消引用时执行的。"

我还没有找到存在不可引用、不可增量输入迭代器的章节。显然,该标准详细说明了copy_n对输入/输出迭代器的取消引用次数,但没有详细说明它对输入迭代器增加了多少次。

天真实现比非天真实现将输入迭代器增加一次。如果我们有一个在空间不足的情况下读取++的单程输入迭代器,copy_n可能会不必要地阻塞进一步的输入,试图读取超过输入流末尾的数据。

这只是一个实现。GCC 4.4中的实现是不同的(并且在概念上更简单):

template<typename InputIterator, typename _Size, typename _OutputIterator>
_OutputIterator
copy_n(_InputIterator __first, _Size __n,
     _OutputIterator __result)
{
  for (; __n > 0; --__n)
{
  *__result = *__first;
  ++__first;
  ++__result;
}
  return __result;
}

[有一点手工,因为我只在输入迭代器是输入迭代程序时提供了实现,所以对于迭代器为随机访问迭代器的情况,有一个不同的实现。]该实现有一个错误,它使输入迭代机的增量比预期的多一倍。

GCC 4.8中的实现有点复杂:

template<typename _InputIterator, typename _Size, typename _OutputIterator>
_OutputIterator
copy_n(_InputIterator __first, _Size __n,
     _OutputIterator __result)
{
  if (__n > 0)
{
  while (true)
    {
      *__result = *__first;
      ++__result;
      if (--__n > 0)
    ++__first;
      else
    break;
    }
}
  return __result;
}

使用naive实现,您可以将输入迭代器n递增,而不仅仅是n - 1。这不仅可能效率低下(因为迭代器可以具有任意且任意昂贵的用户定义类型),而且当输入迭代器不支持有意义的"结束后"状态时,这也可能是完全不可取的。

举个简单的例子,考虑从std::cin:中读取n元素

#include <iostream>    // for std:cin
#include <iterator>    // for std::istream_iterator

std::istream_iterator it(std::cin);
int dst[3];

对于天真的解决方案,程序会阻止最后的增量:

int * p = dst;
for (unsigned int i = 0; i != 3; ++i) { *p++ = *it++; }   // blocks!

标准库算法不会阻塞:

#include <algorithm>
std::copy_n(it, 3, dst);    // fine

请注意,该标准实际上并没有明确表示迭代器增量。它只说(25.3.1/5)copy_n(first, n, result)具有

效果:对于每个非负整数i < n,执行*(result + i) = *(first + i)

24.2.3/3中只有一条注释:

[input迭代器]算法可以与作为输入源的istreams一起使用数据通过CCD_ 21类模板。

由于初始检查

if (count > 0)

我们知道count>0,因此该代码的作者认为,在达到值1之前,他不需要再次对count进行测试。请记住,"for"在每次迭代开始时执行条件测试,而不是在迭代结束时。

Size count = 1;
for (Size i = 1; i < count; ++i) {
    std::cout << i << std::endl;
}

不会打印任何内容。

因此,该代码消除了一个条件分支,如果Size为1,则无需增加/调整"first",因此它是一个预增量。