【问题标题】:Limiting the range for std::copy with std::istream_iterator用 std::istream_iterator 限制 std::copy 的范围
【发布时间】:2011-05-10 17:00:11
【问题描述】:

我构建了一个最小的工作示例来展示我在使用 STL 迭代器时遇到的问题。我正在使用istream_iteratorstd::istream 中读取floatss(或其他类型):

#include <iostream>
#include <iterator>
#include <algorithm>

int main() {
   float values[4];
   std::copy(std::istream_iterator<float>(std::cin), std::istream_iterator<float>(), values);
   std::cout << "Read exactly 4 floats" << std::endl; // Not true!
}

这会读取所有可能的 floatss,直到 EOF 到 values,它的大小是固定的,4,所以现在显然我想限制范围以避免溢出并准确/最多读取 4 个值。

使用更多“正常”迭代器(即 RandomAccessIterator),前提是 begin+4 没有超出您的预期目标:

std::copy(begin, begin+4, out);

准确读取 4 个元素。

如何使用std::istream_iterator 做到这一点?显而易见的想法是将对 std::copy 的调用更改为:

std::copy(std::istream_iterator<float>(std::cin), std::istream_iterator<float>(std::cin)+4, values);

但是(可以预见)这不会编译,operator+ 没有候选人:

g++ -Wall -Wextra test.cc
test.cc: In function ‘int main()’:
test.cc:7: error: no match for ‘operator+’ in ‘std::istream_iterator<float, char, std::char_traits<char>, long int>(((std::basic_istream<char, std::char_traits<char> >&)(& std::cin))) + 4’

有什么建议吗?是否有正确的“STLified” pre-C++0x 方法来实现这一目标?显然,我可以将它写成一个 for 循环,但我希望在这里学习一些关于 STL 的知识。我有点想知道滥用std::transformstd::merge 等以某种方式实现此功能,但我不太明白该怎么做。

【问题讨论】:

  • +1 是一个简短的自包含完整示例(请参阅 sscce.org)。
  • @Rob - 我一直使用术语 MWE 来表示最小的工作示例,但这是一种很好的描述方式,尤其是在该 URL 上使用有用的文本。

标签: c++ stl iterator istream-iterator


【解决方案1】:

看看std::copy_n

【讨论】:

  • @Igor Nazarenko :公平,但您应该注意,该算法是 C++0x 的新算法,在旧编译器的标准库中不可用。
  • 啊,那时 C++0x 部分可能对我来说是个问题,并且可以解释为什么我在 C++98 时代的 STL 书中没有发现它。任何非 C++0x 解决方案?我看看是否有一种巧妙的方式(ab)使用 std::transform 或 std::swap_range 之类的东西来实现这一点,但我看不到任何明显的东西。
  • 使用 copy_n 有它自己的问题,它增加 istream_iterator http://stackoverflow.com/questions/5074122/stdistream-iterator-with-copy-n-and-friends 在这种情况下,它可能会复制 4 个浮点数,但 可能 也从 cin 读取 5 !
  • @ildjarn:来自@awoodland 帖子,他尝试使用它以便为copy 算法指定“适当”范围。除非使用 RandomAccessIterator(无论是使用 + 还是 advance),否则您无法在不实际读取整个范围的情况下构建正确的范围。
  • @ildjarn: advance 适用于 InputIterator,但您无法使用它构建范围。
【解决方案2】:

当您请求非 C++0x 解决方案时,这里有一个替代方案,它使用 std::generate_n 和生成器仿函数而不是 std::copy_n 和迭代器:

#include <algorithm>
#include <string>
#include <istream>
#include <ostream>
#include <iostream>

template<
    typename ResultT,
    typename CharT = char,
    typename CharTraitsT = std::char_traits<CharT>
>
struct input_generator
{
    typedef ResultT result_type;

    explicit input_generator(std::basic_istream<CharT, CharTraitsT>& input)
      : input_(&input)
    { }

    ResultT operator ()() const
    {
        // value-initialize so primitives like float
        // have a defined value if extraction fails
        ResultT v((ResultT()));
        *input_ >> v;
        return v;
    }

private:
    std::basic_istream<CharT, CharTraitsT>* input_;
};

template<typename ResultT, typename CharT, typename CharTraitsT>
inline input_generator<ResultT, CharT, CharTraitsT> make_input_generator(
    std::basic_istream<CharT, CharTraitsT>& input
)
{
    return input_generator<ResultT, CharT, CharTraitsT>(input);
}

int main()
{
    float values[4];
    std::generate_n(values, 4, make_input_generator<float>(std::cin));
    std::cout << "Read exactly 4 floats" << std::endl;
}

如果您愿意,可以将此生成器与 boost::generator_iterator 结合使用,将生成器用作输入迭代器。

【讨论】:

  • 有趣,因为generate_n 保证只执行noperator() 的调用,因此您不会遇到copy_n 可能出现的计数问题。此外,如果专门针对std::cin,可以避免使用谓词并使用一个简单的函数(template &lt;typename T&gt; T get() { T t = T(); std::cin &gt;&gt; t; return t; })。此外,它还可以轻松检查输入 (std::cin) 的状态。
  • 我以非 C++0x 为由接受了这个答案,同时避免了与 copy_n 和输入迭代器讨论的问题。
【解决方案3】:

如果您没有可用的std::copy_n,那么编写自己的很容易:

namespace std_ext { 
template<class InputIterator, class Size, class OutputIterator>
OutputIterator copy_n(InputIterator first, Size n, OutputIterator result) {
    for (int i=0; i<n; i++) {
        *result = *first;
        ++result;
        ++first;
    }
    return result;
}
}

【讨论】:

  • 实际上,您的示例可能存在引起@Bo Person 链接的讨论的错误:即first 被递增n 次,因此读取n+1 个值。 Howard Hinnant 在其中一条评论中发布了 libc++ 补丁的提交,给出了一个没有问题的实现:lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20110221/…
  • @Matthieu:我可能需要和霍华德谈谈这件事。在我看来,“更正”版本返回input+n-1,而它需要返回input+n。在某些情况下,这可能是更有用的行为,但除非我误读,否则它看起来仍然违反了要求。
  • @Jerry :您最后的评论有后续吗?
  • @ildjarn:是的——我误读了代码;它确实(正确地)返回input+n
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-29
  • 2013-11-05
  • 2015-01-05
  • 1970-01-01
  • 2010-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多