Я ожидаю, что следующее сообщение о buf_iter
указывает на символ n
после точки начала. Вместо этого он указывает на последний прочитанный символ. Почему это? т.е. если я делаю in_stream.tellg() до и после copy_n, они отличаются не на n
, а на (n-1)
. Если бы я прочитал символы n
с in_stream.read
, тогда позиция будет продвигаться на n
.
std::istreambuf_iterator<char> buf_iter(in_stream);
std::copy_n(buf_iter, n, sym.begin());
Я посмотрел на реализацию, и это явно делает это специально, пропуская окончательный приращение.
Другой пост здесь упоминает, что приращение от итератора при его подключении, скажем, к cin
, приведет к слишком большому количеству чтений с момента чтения на operator++()
. Это звучит как проблема с cin
- почему не прочитано на operator*()
?
Указывает ли стандарт в любом месте? Документы, которые я видел, не упоминают, что происходит с итератором, и я видел две разные страницы, которые дают "возможные правильные реализации", которые выполняют каждое из действий:
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;
}
а на cplusplus.com у нас есть:
template<class InputIterator, class Size, class OutputIterator>
OutputIterator copy_n (InputIterator first, Size n, OutputIterator result)
{
while (n>0) {
*result = *first;
++result; ++first;
--n;
}
return result;
}
Оба делают n чтений и приводят к тому же содержимому в результате. Однако первый будет только увеличивать "первый" итератор n-1
раз, а второй увеличивает его n
раз.
Что дает? Как написать переносимый код? Я могу использовать tellg
и затем seekg
, но тогда я мог бы просто выполнить цикл вручную (ugh!).
Обратите внимание, что я не пытаюсь читать из итератора после вызова copy_n
, скорее я хочу читать из базового потока после вызова copy_n
, и проблема в том, что copy_n
остается указывать на короткий байт где я ожидал, что это произойдет. Пока я иду с отвратительной, но, по-видимому, переносной:
auto pos = in_stream.tellg();
std::istreambuf_iterator<char> buf_iter(in_stream);
std::copy_n(buf_iter, cl, sym.begin());
in_stream.seekg(pos + cl);
uint64_t foo;
in_stream.read(reinterpret_cast<char *>(&foo), 8);
BTW, в случае его непонятности я пытаюсь избежать копирования данных в буфер, а затем снова в строку sym
.
@DaveS: переходя из моей конкретной проблемы, вот простая программа, которая не выводит то, что я ожидаю, из-за того, что входной итератор не увеличивается в последний раз:
#include <algorithm>
#include <string>
#include <iostream>
#include <fstream>
int main(int argc, const char * argv[])
{
std::ifstream in("numbers.txt");
std::istreambuf_iterator<char> in_iter(in);
std::ostreambuf_iterator<char> out_iter(std::cout);
std::copy_n(in_iter, 3, out_iter);
std::cout << std::endl;
std::copy_n(in_iter, 3, out_iter);
std::cout << std::endl;
std::copy_n(in_iter, 3, out_iter);
std::cout << std::endl;
return 0;
}
Входной файл равен "0123456789\n"
Я получаю:
012
234
456
Из-за побочного эффекта istreambuf_iterator::operator++()
это даст другой результат, если бы был реализован copy_n
, чтобы увеличить входной итератор n
раз.
@aschepler: нужно зафиксировать локальный параметр, но я собираюсь с ним:
std::generate_n(sym.begin(), cl, [&in_stream](){ return in_stream.get(); });