Я пытаюсь кодировать действие, противоположное этому:
std::ostream outs; // properly initialized of course
std::set<int> my_set; // ditto
outs << my_set.size();
std::copy( my_set.begin(), my_set.end(), std::ostream_iterator<int>( outs ) );
должно получиться примерно так:
std::istream ins;
std::set<int>::size_type size;
ins >> size;
std::copy( std::istream_iterator<int>( ins ), std::istream_iterator<int>( ins ) ???, std::inserter( my_set, my_set.end() ) );
Но я застрял с итератором конца - интеграторы ввода не могут использовать std :: advance, и я также не могу использовать два потока с одним и тем же источником ...
Есть ли какой-нибудь элегантный способ решить эту проблему? Конечно, я могу использовать цикл for, но, может быть, есть что-нибудь получше :)





(Отредактировано: я должен был прочитать вопрос поближе ...)
Хотя это несколько подозрительно, вы можете получить примерно правильное поведение, если в файле есть запись, которая «выйдет из строя» в первом цикле, затем сбросит бит ошибки в потоке и начнет читать больше.
Данные без явного размера, но вот так
1 1 2 3 5 8 Fibb
Похоже, что приведенный ниже код делает то, что я имел в виду, по крайней мере, на VS2005 с STLPort.
typedef std::istream_iterator < int, char, std::char_traits ,ptrdiff_t> is_iter; std::copy( is_iter(cin), is_iter(), inserter(my_set,my_set.end())); cin.clear(); std::cin >> instr;
Да, SDG, но когда я хочу использовать другие структуры данных в этом файле / потоке? Я, наверное, должен явно написать здесь, я хочу сохранить еще что-то после этого набора, это причина, по которой я также сохраняю размер.
Errr ... Алгоритм copy_n ()?
«Эта функция является расширением SGI; она не является частью стандарта C++». И это очень верно, по крайней мере, этого не хватает в VS2008. Но симпатичный, я чуть было не хотел винить себя, как это возможно, я не заметил этого;)
Немного посмотрев на это, я не думаю, что чтение непосредственно в набор сработает, так как вам нужно вызвать в нем insert, чтобы фактически добавить элементы (я могу ошибаться, здесь довольно рано). Хотя вкратце взглянув на документацию STL в VS2005, я думаю, что что-то с использованием функции generate_n должно работать, например:
std::istream ins;
std::set<int> my_set;
std::vector<int> my_vec;
struct read_functor
{
read_functor(std::istream& stream) :
m_stream(stream)
{
}
int operator()
{
int temp;
m_stream >> temp;
return temp;
}
private:
std::istream& m_stream;
};
std::set<int>::size_type size;
ins >> size;
my_vec.reserve(size);
std::generate_n(my_vec.begin(), size, read_functor(ins));
my_set.insert(my_vec.begin(), my_vec.end());
Надеюсь, это либо решило вашу проблему, либо убедило вас, что цикл не так уж плох в общем плане.
Зачем использовать вектор в качестве промежуточного звена? Просто используйте генератор для вставки в набор (используя std :: insertter).
Было поздно, когда я сочинял ответ, и тогда он не пришел в голову. Я предполагаю, что вы бы использовали устройство для вставки, хотя для этого по-прежнему требуется создание целого другого класса, поэтому для меня цикл будет проще и, вероятно, тоже будет выглядеть лучше.
Вы можете быть производным от istream_iterator <T> .
Хотя использование Метод генератора Daemin - это еще один вариант, хотя я бы сгенерировал непосредственно в набор, а не использовал бы промежуточный вектор.
#include <set>
#include <iterator>
#include <algorithm>
#include <iostream>
template<typename T>
struct CountIter: public std::istream_iterator<T>
{
CountIter(size_t c)
:std::istream_iterator<T>()
,count(c)
{}
CountIter(std::istream& str)
:std::istream_iterator<T>(str)
,count(0)
{}
bool operator!=(CountIter const& rhs) const
{
return (count != rhs.count) && (dynamic_cast<std::istream_iterator<T> const&>(*this) != rhs);
}
T operator*()
{
++count;
return std::istream_iterator<T>::operator*();
}
private:
size_t count;
};
int main()
{
std::set<int> x;
//std::copy(std::istream_iterator<int>(std::cin),std::istream_iterator<int>(),std::inserter(x,x.end()));
std::copy(
CountIter<int>(std::cin),
CountIter<int>(5),
std::inserter(x,x.end())
);
}
Хотя это очень крутое решение, следует отметить, что просто написать цикл, а не делать этого, будет короче.
Вы использовали dynamic_cast для неполиморфного типа. Это не должно компилироваться
@ Армен Цирунян: Ваш аргумент обречен на провал. Вы правы в своем утверждении, что неполиморфные типы не будут компилироваться при использовании с dynamic_cast. Тем не менее, приведенное выше компилируется и работает правильно. Таким образом, по occams razer мы пришли к выводу, что это полиморфный тип. Спасибо и спокойной ночи. :-)
Как насчет использования альтернативного итератора для выполнения обхода, а затем использования объекта функции (или лямбда-выражения) для заполнения контейнера?
istream ins;
set<int>::size_type size;
set<int> new_set;
ins >> size;
ostream_iterator<int> ins_iter(ins);
for_each(counting_iterator<int>(0), counting_iterator<int>(size),
[&new_set, &ins_iter](int n) { new_set.insert(*ins_iter++); }
);
Конечно, это предполагает, что у вас есть компилятор, совместимый с C++ 0x.
Кстати, counting_iterator <> является частью Boost.Iterator.
Использовать:
std::copy( std::istream_iterator<int>(ins),
std::istream_iterator<int>(),
std::inserter(my_set, my_set.end())
);
Обратите внимание на пустой параметр:
std::istream_iterator<int>();
Черт побери, спасибо! Могу поклясться, что пробовал это, но, похоже, не стал. Отлично работает, большое спасибо! (примечание: всегда здорово следить за собственными сообщениями :)
С другой стороны, как можно определить, например, чтение трех байтов, а не всего файла? ;) Тогда это была моя проблема с принятием вашего ответа, теперь я вспомнил.
Или вы можете сделать это:
my_set.insert(std::istream_iterator<int>(ins), std::istream_iterator<int>());
Хе-хе, вы правы, это вроде подозрительно и самое главное - на двоичном потоке работать не будет :)