Ограничение диапазона для std :: copy с помощью std :: istream_iterator - PullRequest
23 голосов
/ 10 мая 2011

Я создал минимальный рабочий пример, чтобы показать проблему, с которой я столкнулся при использовании итераторов STL.Я использую istream_iterator для чтения floats с (или других типов) из std::istream:

#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!
}

Это читает все возможные floats с, пока EOF в values, чтоимеет фиксированный размер, 4, так что теперь, очевидно, я хочу ограничить диапазон, чтобы избежать переполнения и прочитать ровно / самое большее 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" пре-C ++ 0x способ добиться этого?Очевидно, я мог бы просто записать это как цикл for, но я хочу узнать кое-что о STL здесь.Я наполовину задумался о том, чтобы злоупотреблять std::transform или std::merge и т. Д., Чтобы как-то добиться этой функциональности, но я не совсем понимаю, как это сделать.

Ответы [ 3 ]

14 голосов
/ 10 мая 2011

Поскольку вы запрашивали решение, отличное от 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, чтобы использовать генератор в качестве входного итератора.

13 голосов
/ 10 мая 2011

Взгляните на std::copy_n

3 голосов
/ 10 мая 2011

Если у вас нет 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;
}
}
...