Форум программистов, компьютерный форум, киберфорум
Наши страницы
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
 
kozlik_kozlik
7 / 11 / 0
Регистрация: 01.08.2012
Сообщений: 99
#1

Что-то не так с transform? - C++

17.11.2016, 17:19. Просмотров 823. Ответов 38
Метки нет (Все метки)

transform требует итераторы ввода и делает что-то такое:

C++
1
2
3
4
5
6
7
8
9
template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
                   UnaryOperation unary_op)
{
    while (first1 != last1) {
        *d_first++ = unary_op(*first1++);
    }
    return d_first;
}
И видим здесь:
*first1++

То есть, смещаемся к следующему элементу контейнера, БЕРЁМ СТАРОЕ ЗНАЧЕНИЕ ИТЕРАТОРА, РАЗЫМЕНОВЫВАЕМ... То же самое слева от присваивания. Но: если значение итератора ввода увеличено на единицу, нет никаких гарантий, что его можно
разыменовать по предыдущему значению (с) Прата. Это что же, возможная реализация алгоритма transform с cppreference - неправильная? Или я что-то не так понимаю?
4
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
17.11.2016, 17:19
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Что-то не так с transform? (C++):

Как сделать так что я мог умножать не на два числа а на 3,4,5 и так далее?
Вот код: #include &lt;iostream&gt; #include &lt;conio.h&gt; using namespace std; int...

Что в коде ни так? while не работает так, как ожидаю
Написанный код, как я думаю, должен выдавать цифры от 0 до 1000, столбиком. Но...

Что-то не то с Майкрсофт визуал студио 2010 или я что-то не так делаю
Дело в том что при запуске вот этой программы: #include &lt;iostream&gt; using...

Что в программе делает так, что процессор грузится на 100%?
Я не очень разбираюсь в С++, поэтому прошу вашей подсказки по поводу нагрузки...

Список: Что не так с выводом списка, потому что выводится какой-то мусор?
#include &lt;iostream&gt; using namespace std; struct point { int...

Лучший интерактивный самоучитель с++, но так понравился, что думаю, а что если после python изучать с++?
http://academy.cppstudio.com/courses/ 1.) поделитесь опытом после какого языка...

38
kozlik_kozlik
7 / 11 / 0
Регистрация: 01.08.2012
Сообщений: 99
17.11.2016, 20:37  [ТС] #21
Смысл: нельзя сохранять предыдущее значение итератора.
Как обеспечить: не использовать предыдущее значение итератора.
Каковы могут быть примеры?
На постинкремент это не распространяется, хорошо. Тогда как он реализован? Приведён, насколько понимаю, псевдокод.

Да так, ни о чём. Если надо объяснять, то не надо объяснять.
0
castaway
Эксперт С++
4927 / 3034 / 453
Регистрация: 10.11.2010
Сообщений: 11,089
Записей в блоге: 10
Завершенные тесты: 1
17.11.2016, 20:39 #22
Цитата Сообщение от kozlik_kozlik Посмотреть сообщение
Можете пример привести?
Привести пример чего?
Ответьте на предыдущий вопрос.
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4377 / 2352 / 655
Регистрация: 18.10.2014
Сообщений: 4,002
17.11.2016, 20:45 #23
Цитата Сообщение от castaway Посмотреть сообщение
Смысл: нельзя сохранять предыдущее значение итератора.
Как обеспечить: не использовать предыдущее значение итератора.
И?

Об этом и вопрос! Формально выражение *it++ означает *(it++) и в случае перегруженных операторов * и ++ производится сохранение и использование предыдущего значения итератора после того, как получен инкрементированный итератор.

С точки зрения буквальной интерпретации этого нельзя делать для итераторов класса input iterator. С такими итераторами формально нельзя делать *it++.

Однако в спецификации языка для этих целей сделано специальное замечание, которое добавляет спеициальную трактовку для выражения *it++ в случае input iterator. А именно, такое выражение должно быть экаивалентно

C++
1
2
3
{ T tmp = *r;
++r;
return tmp; }
4
castaway
Эксперт С++
4927 / 3034 / 453
Регистрация: 10.11.2010
Сообщений: 11,089
Записей в блоге: 10
Завершенные тесты: 1
17.11.2016, 20:47 #24
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
в случае перегруженных операторов * и ++ производится сохранение и использование предыдущего значения итератора после того, как получен инкрементированный итератор.
Где производится? Я не вижу.
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4377 / 2352 / 655
Регистрация: 18.10.2014
Сообщений: 4,002
17.11.2016, 20:52 #25
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Однако в спецификации языка для этих целей сделано специальное замечание, которое добавляет специальную трактовку для выражения *it++ в случае input iterator. А именно, такое выражение должно быть эквивалентно

C++
1
2
3
{ T tmp = *r;
++r;
return tmp; }
(см. 24.2.3 Input iterators)

Таким образом исходный код transform корректен, но именно из-за этой специальной обработки выражения *it++ для input iterators.

Общая проблема поднятая kozlik_kozlik действительно имеет место и вопрос задан совершенно справедливый. Ответ на этот вопрос: спецификация стандартной библиотеки содержит "костыль", специально предназначенный для того, чтобы такие случаи работали правильно.

Добавлено через 1 минуту
Цитата Сообщение от castaway Посмотреть сообщение
Где производится? Я не вижу.
Как это где? Внутри реализации постфиксного ++. Постфиксный ++ возвращает старое значение итератора, после чего инкрементирует исходное значение. После этого использовать старое значение уже нельзя. А *it++ посылает это старое значение в оператор *. Отсюда и вопрос.
4
castaway
Эксперт С++
4927 / 3034 / 453
Регистрация: 10.11.2010
Сообщений: 11,089
Записей в блоге: 10
Завершенные тесты: 1
17.11.2016, 20:54 #26
Цитата Сообщение от kozlik_kozlik Посмотреть сообщение
Правильно, если старожил ошибается, это простительно. А новичку можно и нахамить за вопрос. Действительно, чего церемониться.
kozlik_kozlik, вы ответите к чему это было сказано или это клевета?
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4377 / 2352 / 655
Регистрация: 18.10.2014
Сообщений: 4,002
17.11.2016, 21:05 #27
Обратите, кстати, внимание, что спецификация языка разрешает использование только двух типов выражений с постфиксным инкрементом для input iterators:

* (void)r++ - equivalent to (void)++r
* *r++ - определено специальным образом (см. выше)

То есть на самом деле постфиксный ++ формально бесполезен для input iterators - его результат все равно запрещено использовать. Но ради полезной идиомы *r++ было принято решение внести в спецификацию такой "костыль". Фактически *r++ для input iterators - это атомарная операция со своим индивидуальным определением. Она не распадается на независимые * и ++.

Вопрос kozlik_kozlik не просто правильный, а весьма интересный. Я помню, что читал когда-то что-то на эту тему, но мимоходом не до конца понимал, зачем это нужно. А оказывается вот оно что.
1
Toshkarik
1148 / 865 / 90
Регистрация: 03.08.2011
Сообщений: 2,404
Завершенные тесты: 1
17.11.2016, 21:10 #28
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Постфиксный ++ возвращает старое значение итератора, после чего инкрементирует исходное значение.
Наоборот, сначала икрементирует, потом возвращает старое, которое было сохранено в промежутке

Я в принципе так и думал, что последовательность этих 3 операторов как то должна быть обговорена. Сначала подумал про изменение контейнера. Но, тут, как уже сказал TheCalligrapher, об этом сказано явно в стандарте. Если найти причину, почему так сделано, то все станет понятно.
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4377 / 2352 / 655
Регистрация: 18.10.2014
Сообщений: 4,002
17.11.2016, 21:15 #29
Цитата Сообщение от Toshkarik Посмотреть сообщение
Наоборот, сначала икрементирует, потом возвращает старое, которое было сохранено в промежутке
Да, я неточно выразился, под "возвращает старое" я хотел сказать "сохраняет старое значение для последующего возвращения".
0
castaway
Эксперт С++
4927 / 3034 / 453
Регистрация: 10.11.2010
Сообщений: 11,089
Записей в блоге: 10
Завершенные тесты: 1
17.11.2016, 21:56 #30
TheCalligrapher, разыменование итератора что даёт?
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4377 / 2352 / 655
Регистрация: 18.10.2014
Сообщений: 4,002
17.11.2016, 22:04 #31
Цитата Сообщение от castaway Посмотреть сообщение
TheCalligrapher, разыменование итератора что даёт?
Хм... Разыменование итератора даёт нам то значение, на которое итератор ссылается.

(Но к чему это здесь?)
0
castaway
Эксперт С++
4927 / 3034 / 453
Регистрация: 10.11.2010
Сообщений: 11,089
Записей в блоге: 10
Завершенные тесты: 1
17.11.2016, 22:08 #32
Это я к тому, что вы говорите о "старом" и "новом" значении итератора, хотя этот, уже известный вам кусок кода:
C++
1
2
3
value_type x = *i;
++i;
return x;
говорит что нет никакого "старого" итератора.
Есть значение итератора, а есть значение на которое он ссылается.
0
Mathist
44 / 44 / 28
Регистрация: 18.04.2014
Сообщений: 100
Завершенные тесты: 1
17.11.2016, 22:12 #33
TheCalligrapher, не понимаю почему он сначала инкрементирует, а потом возвращает старое, если постфиксная операция должна увеличивать итератор после вычисления выражения. Я не читал стандарт, но мне казалось что возвращается текущее значение, а затем увеличивается, и таким образом нет возвращения предыдущего значения итератора.
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4377 / 2352 / 655
Регистрация: 18.10.2014
Сообщений: 4,002
17.11.2016, 22:14 #34
Цитата Сообщение от castaway Посмотреть сообщение
говорит что нет никакого "старого" итератора.
Ну так я о том и говорю, что в стандартной билиотеке выражению *r++ для input iterator r придано специальное особенное значение, выражаемое вашим куском кода.

При этом в общем случае выражение *r++ такой семаники не имеет даже отдаленно. В общем случае такое выражение имеет семантику

C++
1
2
iterator_type old_r = r++;
return *old_r;
Вопрос ТС потому и возник, что ТС не знал, что в спецификацию языка для input iterator вписан специальный "костыль", переиначивающий трактовку *r++ именно и только для input iterators.
0
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
7004 / 3297 / 449
Регистрация: 04.12.2011
Сообщений: 9,130
Записей в блоге: 5
17.11.2016, 22:20 #35
TheCalligrapher, это удивительно:
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
C++
1
{ T tmp = *r; ++r; return tmp; }
А чем плохо:
C++
1
2
3
4
container<T>::iterator tmp_it = r;
r++;
T val = *tmp_it; 
//----
Временный итератор должен скопировать "предыдущее" значение итератора. Почему инкрементирование должно повлиять на полученный tmp_it ?
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4377 / 2352 / 655
Регистрация: 18.10.2014
Сообщений: 4,002
17.11.2016, 22:37 #36
Цитата Сообщение от IGPIGP Посмотреть сообщение
Почему инкрементирование должно повлиять на полученный tnp_it ?
Потому что так сказно в спецификации input iterator.

Input iterator предназначен для итерования "однопроходных" входных последовательностей. За таким итератором может скрываться поток ввода с клавиатуры или чтения с накопителя на манитной ленте. После того, как вы прочитали данные, вы уже либо принципиально не можете прочитать их снова (нельзя заставить пользователя напечатать еще раз то, что он печатал ранее), либо это слишко дорого (дорого перематывать магнитную ленту назад), либо вы просто настаиваете на однопроходной природе алгоритма.

Размеется, можно предложить хранить считанные данные внутри самого итератора. Тогда будет создаваться иллюзия, как будто через "старые" итераторы можно запросто читать данные. Но это не соответстстует идее итератора. Итератор не должен (не может, не обязан) "кэшировать" читаемые данные внутри себя по многим разным причинам.

Добавлено через 9 минут
Цитата Сообщение от Mathist Посмотреть сообщение
не понимаю почему он сначала инкрементирует, а потом возвращает старое, если постфиксная операция должна увеличивать итератор после вычисления выражения.
Не должна. В спецификации префиксного и постфиксного ++ нет никаких намеков на то, в какой момент происходит модификация операнда.

Спецификации префиксного и постфиксного говорят, что префиксный ++ возвращает новое значение операнда, а постфиксный ++ возвращает старое значение операнда. И все.

А уж как у них там внутри все делается - это не оговаривается. В какой момент постфиксный ++ изменит свой операнд тоже не оговаривается. Главное, чтобы он вернул старое значение.

Поэтому ваше предположение, что постфиксный ++ должен "увеличивать итератор после вычисления выражения" совершенно не обосновано. Ничего подобного никогда в спецификации постфиксного ++ не было.
3
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
7004 / 3297 / 449
Регистрация: 04.12.2011
Сообщений: 9,130
Записей в блоге: 5
17.11.2016, 22:42 #37
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Потому что так сказно в спецификации input iterator.
Спасибо, теперь понятно.
0
castaway
Эксперт С++
4927 / 3034 / 453
Регистрация: 10.11.2010
Сообщений: 11,089
Записей в блоге: 10
Завершенные тесты: 1
17.11.2016, 23:02 #38
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
При этом в общем случае выражение *r++ такой семаники не имеет даже отдаленно.
Увы, в обсуждаемой теме не общий случай.
0
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4377 / 2352 / 655
Регистрация: 18.10.2014
Сообщений: 4,002
17.11.2016, 23:51 #39
Цитата Сообщение от castaway Посмотреть сообщение
Увы, в обсуждаемой теме не общий случай.
Так поэтому-то вопрос ТС и появился, как я его понял. Input iterator - это весьма особенный случай. А реализация transform "почему-то" об этом не беспокоится и спокойно реализует свою функциональность так, как будто ничего особенного в input iterator нет.
0
17.11.2016, 23:51
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
17.11.2016, 23:51
Привет! Вот еще темы с решениями:

Как сделать так,что бы при введение,например 0;0 выдавалось сообщение,что точка находится на границе?
#include &lt;iostream&gt; using namespace std; int main(){ double...

Ребят Незнаю что делать при запуске на компилирование выдает ошибку.Кто понимает что не так скажите а если не затруднит исправьте ошибку
Разговаривал с двумя преподами говорят что проект не правильно создавал,хотя...

Std::transform
Доброе утро! Буду признателен за помощь в следующей проблемке. Я хочу...

что не так?
#include &lt;iostream.h&gt; #include &lt;conio.h&gt; #include &lt;string.h&gt; #include...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
39
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru