从std :: copy和std :: copy_n中提取输入迭代器

Vla*_*bin 7 c++ iterator stl

我试图实现一个反序列化方法,它接受一个输入迭代器并执行一系列块读取(使用std::copystd::copy_n).像这样的东西(只是一个例子):

template <class InputIt>
InputIt unserialize(InputIt it)
{
  std::copy_n(it, sizeof(header_type), reinterpret_cast<char*>(&header));
  std::copy_n(it, header.payload_size, std::back_inserter(payload));
  it = optional.unserialize(it);
  return it;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下如何提前输入迭代器,以便每次跟随调用std::copy_n继续从中读取并最终返回它?

出于性能原因,我希望对迭代器类别(尤其是RandomAccessIterator和InputIterator)具有通用性,并希望可以使用std::copy方法而无需重写这些方法.像绑定检查这样的东西将由迭代器适配器完成,或者如果已知大小,则在反序列化调用之前检查.

什么不起作用也不可接受:

  1. 使用std::copy_n<InputIt&>(it, ...)可能适用于某些类别但不适用于所有类别,并且它太不可靠.
  2. std::advance在每次调用之后使用会导致为某些迭代器重新读取相同的块.不是优选的,对某些来源可能是不可能的.

UPDATE使得迭代器引用适配器没有帮助,因为随机访问迭代器版本的copy_n返回指针超过复制的最后一个元素,而输入迭代器版本返回指向复制的最后一个元素的指针.所以我想自己的版本copy_n最适合使用额外的迭代器适配器进行绑定检查.

Pio*_*ycz 2

对于随机访问迭代器,可以使用这种形式 - 而且很好:

template <class InputIt, class N, class OutputIt>
InputIt copy_n_advance_input(InputIt it, N dist, OutputIt outIt)
{
    std::copy_n(it, dist, outIt);
    return std::next(it, dist);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是 - 问题是当我们想要处理一次输入迭代器时 - 就像这里(得到“d” - 而不是“c”):

std::string s = "abcd";
std::istringstream ss{s};
auto e = copy_n_advance_input(std::istream_iterator<char>(ss), 
                             2, 
                             std::ostream_iterator<char>(std::cout, ","));
std::cout << "\n" << *e << "\n";
Run Code Online (Sandbox Code Playgroud)

因此,像 STL 中通常那样,似乎需要两种形式:

template <class InputIt, class N, class OutputIt>
InputIt copy_n_advance_input_impl(InputIt it, N dist, OutputIt outIt,
                                  std::input_iterator_tag)
{
    while (dist-- > 0)
    {
        *outIt = *it;
        ++outIt;
        ++it;
    }
    return it;
}

template <class InputIt, class N, class OutputIt>
InputIt copy_n_advance_input_impl(InputIt it, N dist, OutputIt outIt, 
                                  std::random_access_iterator_tag)
{
    std::copy_n(it, dist, outIt);
    return std::next(it, dist);
}

template <class InputIt, class N, class OutputIt>
InputIt copy_n_advance_input(InputIt it, N dist, OutputIt outIt)
{
    return copy_n_advance_input_impl(it, dist, outIt, typename std::iterator_traits<InputIt>::iterator_category {});
}
Run Code Online (Sandbox Code Playgroud)

请注意,建议的版本std::input_iterator_tag不如 STL 有效(至少对于 gcc 而言) - 它对输入进行额外的迭代 - 此迭代对于执行复制不是必需的 - 但需要返回“复制后”范围的开头( stl_algo.h):

754   template<typename _InputIterator, typename _Size, typename _OutputIterator>
755     _OutputIterator
756     __copy_n(_InputIterator __first, _Size __n,
757          _OutputIterator __result, input_iterator_tag)
758     {
759       if (__n > 0)
760     {
761       while (true)
762         {
763           *__result = *__first;
764           ++__result;
765           if (--__n > 0)
766         ++__first;
767           else
768         break;
769         }
770     }
771       return __result;
772     }
Run Code Online (Sandbox Code Playgroud)

最后一点 - 对于随机访问迭代器(如 std::vector::iterator),使用仅调用 std 算法的版本是更明智的 - 因为它们可以更加优化 - 例如,对于 POD 类型上的连续内存迭代器 -这可以只是 memcpy'ied。std::vector<bool>或者存在某种专门化std::deque<T>,使用其内部结构以最有效的方式执行复制。