Здравствуйте, Evgeniy13, Вы писали:
E>у нас в проекте во многих местах есть проверки, что malloc вернет ноль — это лишь откладывает ошибку на некоторое (не такое уж и большое время), а если разрабатывать суперстабильную систему, то все равно нужно как-то обрабатывать подобные исключительные ситуации (тогда какая разница: поймать исключение или проверить результат функции?)... а может вообще vector, например, должен _молча_ выделить столько, сколько сможет и все?!
Насчет разницы.
Возвращаемое значение легче увидеть в коде, особенно если делать последовательно все функции с возвратом ошибок. В С это делается автоматически, так как возвращаемое значение плюс возможно что-то вроде errno, это практически единственный механизм обработки ошибок, не считая экзотического setjmp, который я видел только в одном Open Source проекте (http://www.lua.org/pil/24.3.html).
Игнорирование возвращаемого значения легко заметить с помощью ключа -Wall и т.п.
При использовании исключений это менее заметно, отчасти из-за отсутсвия диагностики. А замечать это нужно, так как выбрасывание исключений (впрочем, как и преждевременный возврат из функции по ошибке), может легко нарушить инварианты.
Например, при использовании возвращаемого значения часто забывают освободить ранее выделенный в этой функции ресурс. С++ эту проблему решает с помощью RAII. Но к сожалению, она не все случаи охватывает. Типичный пример, это последовательное добавление элемента в различные контейнеры внутри одного объекта. Кто-нибудь делает откат, вызывая pop/pop_front/pop_back после выбрасывания исключений? Некоторые используют ScopeGuard и это хорошо, но он тоже не все случаи покрывает.
[]
А>>>10.4. Конкатенация строк обозначается знаком '+'. Ещё одна программистская традиция, идущая вразрез с математикой. В MFC (и много где ещё) то же самое. В Visual Basic-е для этого используется '&', в script-овом языке Lua — '..' (две точки). А>>>Вместо бинарного operator+, сцепляющего строки по очереди, было бы оптимальнее (по скорости работы программы) использовать функцию Concat, сцепляющую несколько строк одним махом. К счастью, такую функцию (точнее, семейство функций) можно сделать самому. Например: RO>>Здесь более в духе STL было бы что-то вроде join_iterator. Тоже можно самому написать. ПС>Итератор нужен для доступа к последовательности. Вы какую последовательность имели в виду? Я имел в виду, что вместо: ПС>
ПС>DirPath + "\\" + FileName
ПС>
ПС>было бы оптимальнее писать: ПС>
ПС>Concat(DirPath, "\\", FileName)
ПС>
чем оптимальнее? и тут и там идет вызов функции, просто одна из них это operator+. Кроме того, такой Concat можно реализовать только через ellipsis, которые есть зло.
ПС>Насколько я знаю, компилятор C# делает такую замену автоматически.
Здравствуйте, kan, Вы писали:
kan>Kluev wrote:
>> for(i = 0, j = vec.size()-1; i < j; i++)
>> Со знаковыми типами вот такой код будет нормально работать, а с >> бесзнаковыми i,j при vec.size()==0 будут грабли. kan>А зачем такой ужас писать? Чем банальный
kan>for(size_t i = 0; i + 1 < vec.size(); i++) kan>не устраивает?
Такая форма записи неестественная и не соотвествует ходу мышления.
>> R>зачем в вашем примере GetPos возвращает int? судя по всему только для >> возможности задать спец значение -1, на мой взгляд в таком случае более >> правильно использовать явно объявленое значение для беззнакового — как >> например std::string::npos. >> >> Для того чтобы можно было писать по человечески. Если например двигаемся >> в произвольном направлении и с произвольным шагом, то к индексу kan>Для этого есть iterator.
С итераторами неудобно работать.
1) они становятся невалидными после ресайза
2) программу неудобно отлаживать, т.к. итератор ничего не говорит ни о номере итерации, ни о индексе элемента в массиве.
Kluev wrote:
>> > for(i = 0, j = vec.size()-1; i < j; i++) > >> > Со знаковыми типами вот такой код будет нормально работать, а с >> > бесзнаковыми i,j при vec.size()==0 будут грабли. > kan>А зачем такой ужас писать? Чем банальный > > kan>for(size_t i = 0; i + 1 < vec.size(); i++) > kan>не устраивает? > Такая форма записи неестественная и не соотвествует ходу мышления.
Мышление переделай. Но запись абсолютно естественна — проверить, что выражение "i+1", которое мы используем как индекс,
не выходит за пределы размера вектора. А что означает "i < vec.size() — 1", особенно в случае нулевого размера?
Ещё прошу заметить, что понятие отрицательного индекса — вообще противоестественна, а поэтому может трактоваться по
разному. Например, в перле, обратиться по индексу "-2" — валидная операция, означает взять второй элемент с конца.
>> > R>зачем в вашем примере GetPos возвращает int? судя по всему только для >> > возможности задать спец значение -1, на мой взгляд в таком случае более >> > правильно использовать явно объявленое значение для беззнакового — как >> > например std::string::npos. >> > >> > Для того чтобы можно было писать по человечески. Если например двигаемся >> > в произвольном направлении и с произвольным шагом, то к индексу > kan>Для этого есть iterator. > С итераторами неудобно работать. > 1) они становятся невалидными после ресайза > 2) программу неудобно отлаживать, т.к. итератор ничего не говорит ни о > номере итерации, ни о индексе элемента в массиве.
Индекс для вектора всегда "iter — vec.begin()"
В общем, какие-то надуманные проблемы, чаще всего возникающие от недопонимания понятия "итератор".
Posted via RSDN NNTP Server 2.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, kan, Вы писали:
>> DirPath + "\\" + FileName >> >> было бы оптимальнее писать: >> >> Concat(DirPath, "\\", FileName) kan>Но в С++ нет функций с переменным числом аргументов (ellipsis не считается).
Здравствуйте, kan_izh, Вы писали:
_>Шахтер wrote:
>> Ш>>3) Контейнеры. Все STL контейнеры расчитаны на копирабельные типы. >> Это огромный минус. >> >> _>это элементарно обходится: используй контейнеры на SmartPtr, а сам >> указатель может быть и некопирабельным. >> >> >> И аллокировать каждый объект в динамической памяти? >> Нет, спасибо. _>А как ты тогда себе представляешь динамический массив без возможности скопировать элемент?
Я его не представляю. Я его использую.
_>Вроде только если будут move constructor... это обещают в следующей версии языка... но...
Да не нужна новая версия языка для этого. Всё прекрасно можно сделать на старой.
Здравствуйте, kan, Вы писали:
kan>Roman Odaisky wrote: >> А>2. Все STL-контейнеры неявно копируются. >> Ориентируясь на забывчивых программеров, каши не сваришь. Хотя что-то в >> идее есть... Как только быть с функциями, возвращающими контейнер по >> значению? kan>Да нет, здесь всё ок. Тем более если осталось незамеченным — ради бога, пусть копируется жалко что-ли? Вдруг там список kan>обычно из 3 элементов и вызывается это раз в пятилетку? А если это источник тормозов — профайлер такие вещи показывает kan>на раз.
Лучше, чтобы было предупреждение компилятора: "STL-контейнер, не string, передаётся по значению". А программист пускай решает: подавить это предупреждение или повысить до уровня ошибки.
Профайлер — run-time инструмент, изучает конкретный сценарий работы программы. Скажем, в домашних условиях программа тестируется с поверхностью, в которой 100 .. 1000 точек. Не тормозит. А пользователь, например, работает с поверхностью, в которой 100 000 точек. Идеология C++ такая: "что-то в compile-time лучше, чем что-то в run-time". Поэтому лучше узнать о проблеме от компилятора, а не от профайлера.
Я привёл пример, когда неявное копирование STL-контейнера вызвало сильное замедление работы программы. Замедление было замечено. Но люди на форуме смотрели на код в упор и не видели причину (на самом деле там была ещё одна причина: скобки glBegin/glEnd стояли внутри цикла; это заметили).
>> А>6. Вместо метода empty удобнее был бы метод с позитивным смыслом. >> ага kan>Фиолетово. Да и к тому же вариант с isEmpty или подобным — наиболее распространён.
На вкус и цвет товарищей нет. Чем меньше отрицаний, тем понятнее программа. А то иногда бывает код, как в песне Бориса Гребенщикова: "Я не знаю никого, кто не против".
У .NET-контейнеров (там они называются 'collections') я не обнаружил метода вроде empty.
kan>"if(cont.size())", "если обладает размером"
Здесь осторожнее. Есть такие реализации STL, что list::size работает за время O(N) (N — количество элементов списка).
>> А>7. Нельзя const_cast const_iterator в iterator. >> Ну, это, если и проблема, то не STL, а языка. kan>Я бы тоже не отказался, но не в таком виде, а в виде: kan>
kan>Cont cont = makeContainer();
kan>Cont::const_iterator iter = chooseSomeElement(cont); // chooseSomeElement берёт на вход const Cont&, что логично, и
kan>может вернуть только const_iterator
kan>Cont::iterator nonconst_iter = cont.make_nonconst(iter);// Вот такое хочу!
kan>cont.erase(nonconst_iter);// иначе, например, такое не сделать. :(
kan>
В данном случае, если есть возможность менять модуль с chooseSomeElement, то можно выкрутиться:
// не меняет Elements
Cont::iterator chooseSomeElement(Cont& Elements)
{
// вся работа здесь
...
}
// обёртка над не-const chooseSomeElement
Cont::const_iterator chooseSomeElement(const Cont& Elements)
{
// временно убрать константностьreturn chooseSomeElement(const_cast<Cont&>(Elements));
// iterator неявно преобразуется в const_iterator
}
Здравствуйте, Константин Л., Вы писали: А>>>>10.4. Конкатенация строк ПС>>Я имел в виду, что вместо: ПС>>
ПС>>DirPath + "\\" + FileName
ПС>>
ПС>>было бы оптимальнее писать: ПС>>
ПС>>Concat(DirPath, "\\", FileName)
ПС>>
КЛ>чем оптимальнее? и тут и там идет вызов функции, просто одна из них это operator+. Кроме того, такой Concat можно реализовать только через ellipsis, которые есть зло.
operator+ бинарный. Поэтому в первом варианте сцепление строк делается в два этапа, примерно так:
То есть сколько плюсов, столько и вызовов. При этом создаются временные буферы (в данном случае один). До последнего operator+ char-ы хранятся во временных буферах. Только последний operator+ создаёт результирующий буфер и копирует туда char-ы.
Функция Concat, в идеале, "сколько-угодно-арная". В моей реализации (здесь
) используется класс StringBuf. При REALISTIC_STL_IMPL Concat не создаёт временных буферов, а сразу же создаёт результирующий буфер и копирует туда все char-ы.
Здравствуйте, kan, Вы писали:
kan>Пётр Седов wrote:
>> NULL-итератор >> То, что для пустого vector-а эти два итератора равны, проблем создавать >> не должно. >> Можно использовать boost::optional<iterator>, но лучше не усложнять. kan>Не понял, а чем std::vector<X>::const_iterator() не устраивает?
Если итератор — класс, а не указатель, то он так и останется неинициализированным. Для list-а, например, код:
list<int>::iterator it = list<int>::iterator(); // конструктор по умолчанию
*it = 10;
будет работать примерно так же, как и этот:
list<int>::iterator it; // конструктор по умолчанию
*it = 10;
В обоих случаях: если повезёт, будет громкий access violation; если не повезёт, будет тихая порча памяти.
'std::vector<X>::const_iterator()' — слишком многословно, 'NULL' — короче (или даже '0').
>> Я предупредил начинающих, что под size_t грабли лежат. А уж использовать его или нет — пускай сами решают. kan>Тут проблема не в STL, а знании начинающими арифметики и понимании операции вычитания. Всегда можно a == b — 1 преобразовать в a + 1 == b
Неужели есть программисты, не знающие арифметику? Проблема в том, что беззнаковая арифметика отличается от обычной в районе нуля. А знаковая арифметика для большого диапазона чисел по обе стороны от нуля совпадает с обычной. Даже если у Вас есть гигантская структура данных, требующая size_t, то скорее всего она изолирована в каком-то классе (например, менеджер памяти). И уж точно не стоит использовать STL для такой структуры данных. Представьте: string с буфером около 2 GB, возвращаете его из функции, при этом делается deep copy.
>> А>>5. Нельзя получить итератор по указателю на элемент контейнера за >> константное время. >> проигрывает ручному списку в стиле языка C: kan>Это называется intrusive containers
Я писал именно про STL, не про Boost.
>> А>>8. vector >> А>>8.1. Странное название у этого контейнера. >> RO>если еще учесть std::tr1::array = boost::array, то вообще весело станет >> RO>Это не так важно, все привыкли уже. >> + valarray >> Да, все привыкли к ещё одной программистской традиции, идущей вразрез с >> математикой. Одно из самых сложных действий в программировании — >> придумать хорошее имя для сущности. 'vector' — неудачное имя для >> динамического массива. Но это разговор для другого форума. kan>Я уже объяснял. Array — общее название массивов. Одномерный array называется vector (вспомни алгебру — там есть даже два понятия — вектор-столбец и вектор-строка), двумерный — matrix. В STL vector есть одномерный array, так что наоборот, всё очень точно.
Проблема не практическая, а философская. Уж лучше:
vector -> dynarray (обобщённый динамический массив)
valarray -> vector (для ВычМата, с расчётом на аппаратное ускорение)
Так когда-то и планировалось (я привёл историческую справку здесь
).
>> DirPath + "\\" + FileName >> было бы оптимальнее писать: >> Concat(DirPath, "\\", FileName) kan>Но в С++ нет функций с переменным числом аргументов (ellipsis не считается). И ради одной только операции конкатенации вводить такую штуку?..
Я сделал Concat-ы до 5-ти строк:
Пока хватало. Не хватит — допишу.
>> Насколько я знаю, компилятор C# делает такую замену автоматически. kan>Хаки? Или как там это работает?
Там тип string встроен в язык. Например, здесь
Здравствуйте, kan, Вы писали:
kan>Kluev wrote: >> kan>А зачем такой ужас писать? Чем банальный >> >> kan>for(size_t i = 0; i + 1 < vec.size(); i++) >> kan>не устраивает? >> Такая форма записи неестественная и не соотвествует ходу мышления. kan>Мышление переделай. Но запись абсолютно естественна — проверить, что выражение "i+1", которое мы используем как индекс не выходит за пределы размера вектора.
Ничего естесвенного в такой записи нет, это просто вынужденный воркэраунд вокруг беззнаковых типов.
Условие в цикле должно быть простым и ясным: итерировать отсюда и до сюда. "i+1" добавляет третье условие которое программист учитывать при чтении кода. Четвертое условие которе так же прийдется иметь ввиду чтобы не наступить на грабли — беззнаковость size_t.
kan> А что означает "i < vec.size() — 1", особенно в случае нулевого размера?
Означает итерировать все кроме последнего. Читается точно так же как и думается.
kan>Ещё прошу заметить, что понятие отрицательного индекса — вообще противоестественна, а поэтому может трактоваться по kan>разному. Например, в перле, обратиться по индексу "-2" — валидная операция, означает взять второй элемент с конца.
Пусть в перле делают как хотят, а в с++ индексация связана с адрессной арифметикой и поэтому должна иметь аналогичное поведение. В адресной арифметике отрицательные индексы совершенно естественны (указатель может указывать на середину массива)
>>> > Для того чтобы можно было писать по человечески. Если например двигаемся >>> > в произвольном направлении и с произвольным шагом, то к индексу >> kan>Для этого есть iterator. >> С итераторами неудобно работать. >> 1) они становятся невалидными после ресайза >> 2) программу неудобно отлаживать, т.к. итератор ничего не говорит ни о >> номере итерации, ни о индексе элемента в массиве.
kan>Индекс для вектора всегда "iter — vec.begin()"
В отладчике это как посмотреть? Или условный брейкпоинт поставить? В общем случае никак.
kan>В общем, какие-то надуманные проблемы, чаще всего возникающие от недопонимания понятия "итератор".
Итераторы — это костыли и грабли под видом красивой идеи. Для массивов — лучше юзать индексы, для интрузивных node-based контейнеров достаточно знать указатель на элемент. Лишние сущности не нужны.
Здравствуйте, Пётр Седов, Вы писали:
ПС>Здравствуйте, Константин Л., Вы писали: А>>>>>10.4. Конкатенация строк ПС>>>Я имел в виду, что вместо: ПС>>>
ПС>>>DirPath + "\\" + FileName
ПС>>>
ПС>>>было бы оптимальнее писать: ПС>>>
ПС>>>Concat(DirPath, "\\", FileName)
ПС>>>
КЛ>>чем оптимальнее? и тут и там идет вызов функции, просто одна из них это operator+. Кроме того, такой Concat можно реализовать только через ellipsis, которые есть зло. ПС>operator+ бинарный. Поэтому в первом варианте сцепление строк делается в два этапа, примерно так: ПС>
ПС>То есть сколько плюсов, столько и вызовов. При этом создаются временные буферы (в данном случае один). До последнего operator+ char-ы хранятся во временных буферах. Только последний operator+ создаёт результирующий буфер и копирует туда char-ы. ПС>Функция Concat, в идеале, "сколько-угодно-арная". В моей реализации (здесь
) используется класс StringBuf. При REALISTIC_STL_IMPL Concat не создаёт временных буферов, а сразу же создаёт результирующий буфер и копирует туда все char-ы.
это все понятно, только вот это временные буферы меня не смущают, в отличие от ваших (вполне пригодных) велосипедов
Здравствуйте, Kluev, Вы писали:
kan>>В общем, какие-то надуманные проблемы, чаще всего возникающие от недопонимания понятия "итератор".
K>Итераторы — это костыли и грабли под видом красивой идеи. Для массивов — лучше юзать индексы, для интрузивных node-based контейнеров достаточно знать указатель на элемент. Лишние сущности не нужны.
Здравствуйте, Юнусов Булат, Вы писали:
ЮБ>Здравствуйте, Kluev, Вы писали:
kan>>>В общем, какие-то надуманные проблемы, чаще всего возникающие от недопонимания понятия "итератор".
K>>Итераторы — это костыли и грабли под видом красивой идеи. Для массивов — лучше юзать индексы, для интрузивных node-based контейнеров достаточно знать указатель на элемент. Лишние сущности не нужны.
ЮБ>Ты себе представляешь stl без итераторов?
Цель итераторов — сделать перебор элементов в разных контейнерах одинаковым. Это больная идея т.к. контейнеры должны перебиратся наиболее нативным для них способом. Как например должен выглядеть итератор для многомерных массивов, кольцевых списков и деревьев? Многие контейнеры в концепцию итераторов просто не вписываются в результате чего программист вынужден пользоватся тем убогим набором который есть в stl. К томуже алгоритмы применимы не ко всем контейнерам и фактически единственным универсальным алгоритмом который подходит для всех является for_each. Но для этого существует более универсальный и могучий паттерн — визитор, который:
1) гораздо проще в реализации чем итератор
2) программы с его использованием получаются более элегантными
Третье преимущество в том что внутри container.clear(clear_func) пербор идет способом родным для контейнера, что существенно упрощает отладку.
Так же итераторы в стиле stl совершенно не подходят для перебора кольцевых контейнеров.
Т.к. в силу замкнутости их требуется итерировать не в диапазоне [begin, end), а в диапазоне [begin, last]
Elem *p, *last; // если p == last делаем полный кругfor (bool ok = true; ok; p = next(p))
{
ok = p != last;
// .....
}
Kluev wrote:
> Ничего естесвенного в такой записи нет, это просто вынужденный > воркэраунд вокруг беззнаковых типов. > Условие в цикле должно быть простым и ясным: итерировать отсюда и до > сюда. "i+1" добавляет третье условие которое программист учитывать при > чтении кода. Четвертое условие которе так же прийдется иметь ввиду чтобы > не наступить на грабли — беззнаковость size_t.
Почитай теоритические основы программирования. Предусловия, постусловия, инвариант цикла, схемы программ Янова, триады
Хоара. Тогда появится осмысление того чего ты пишешь и что является "естественным".
> kan> А что означает "i < vec.size() — 1", особенно в случае нулевого > размера? > Означает итерировать все кроме последнего. Читается точно так же как и > думается.
Какой элемент последний в пустом массиве?
> Пусть в перле делают как хотят, а в с++ индексация связана с адрессной > арифметикой и поэтому должна иметь аналогичное поведение. В адресной > арифметике отрицательные индексы совершенно естественны (указатель может > указывать на середину массива)
Кто сказал что связана? В stl не связана. Есть операция взятия элемента по индексу, индекс — неотрицательное число. Где
ты видел упоминание адресной арифметики в std::vector?
> kan>Индекс для вектора всегда "iter — vec.begin()" > В отладчике это как посмотреть? Или условный брейкпоинт поставить? В > общем случае никак.
Ну это проблемы отладчика, а не stl. А вообще говоря можно: "iter._Myptr — vec._Myfirst" для MSVS7.1 с родным STL.
> kan>В общем, какие-то надуманные проблемы, чаще всего возникающие от > недопонимания понятия "итератор". > Итераторы — это костыли и грабли под видом красивой идеи. Для массивов — > лучше юзать индексы, для интрузивных node-based контейнеров достаточно > знать указатель на элемент. Лишние сущности не нужны.
Ты вообще понимаешь смысл понятия "generic programming"? Смысл абстракции?
Видишь ли, stl основывается на некоторых теоретических аспектах, а не только на общеизвестных практических приёмчиках.
Posted via RSDN NNTP Server 2.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Kluev wrote:
> ЮБ>Ты себе представляешь stl без итераторов? > Цель итераторов — сделать перебор элементов в разных контейнерах > одинаковым. Это больная идея т.к. контейнеры должны перебиратся наиболее > нативным для них способом. Как например должен выглядеть итератор для > многомерных массивов, кольцевых списков и деревьев? Многие контейнеры в
Делается просто — несколько типов итераторов. Классический пример — begin-end/rbegin-rend. Для дерева можно 2 типа
сделать — обход в ширину и обход в глубину.
Т.е. функции, возвращающие итераторы могут быть даже с аргументами, например, задать по какой "кривой" обойти
многомерный массив.
> концепцию итераторов просто не вписываются в результате чего программист > вынужден пользоватся тем убогим набором который есть в stl. К томуже > алгоритмы применимы не ко всем контейнерам и фактически единственным > универсальным алгоритмом который подходит для всех является for_each. Но > для этого существует более универсальный и могучий паттерн — визитор, > который: > > 1) гораздо проще в реализации чем итератор > 2) программы с его использованием получаются более элегантными
Требует задания функтора. Всегда. А это не всегда элегантно.
Не позволяет иметь несколько итераторов, позволяющих обходить контейнер в разном порядке одновременно.
> Третье преимущество в том что внутри container.clear(clear_func) пербор > идет способом родным для контейнера, что существенно упрощает отладку.
Какой способ является родным для дерева? Да даже для того же массива? Почему от начала к концу, а не наоборот? Или с
середины к краям?
> Так же итераторы в стиле stl совершенно не подходят для перебора > кольцевых контейнеров. > Т.к. в силу замкнутости их требуется итерировать не в диапазоне [begin, > end), а в диапазоне [begin, last]
[begin, last] никак не позволяет задавать пустые интервалы, и никак это не обойти, и это является наиболее важным. Тем
более не надо делать замкнутый итератор единственно доступным для контейнера. Пусть это будет обычный std::list, просто
для него создать ещё один тип итератора, который будет замыкаться. Но это не запрещает при необходимости использовать
старые добрые begin/end. Можно написать метод, возвращающий [first, last) для кольцевого контейнера, притом first будет
совпадать с указанным "началом" кольца.
На самом деле есть более интересная абстракция boost::range.
Posted via RSDN NNTP Server 2.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, kan, Вы писали:
kan>Kluev wrote:
>> Ничего естесвенного в такой записи нет, это просто вынужденный >> воркэраунд вокруг беззнаковых типов. >> Условие в цикле должно быть простым и ясным: итерировать отсюда и до >> сюда. "i+1" добавляет третье условие которое программист учитывать при >> чтении кода. Четвертое условие которе так же прийдется иметь ввиду чтобы >> не наступить на грабли — беззнаковость size_t. kan>Почитай теоритические основы программирования. Предусловия, постусловия, инвариант цикла, схемы программ Янова, триады kan>Хоара. Тогда появится осмысление того чего ты пишешь и что является "естественным".
Отсылать к литературе — это плохая идея. Если хочешь продвинуть мысль то лучше изложи суть идеи, преимущества, недостатки. Мнения классиков меня не волнуют.
>> kan> А что означает "i < vec.size() — 1", особенно в случае нулевого >> размера? >> Означает итерировать все кроме последнего. Читается точно так же как и >> думается. kan>Какой элемент последний в пустом массиве?
Фраза итерировать все кроме последнего сотоит из двух частей.
1) итерировать все
2) кроме последнего
В пустом массиве элементов нет значит вторая часть фразы нас уже не интересует. логика проста и очевидна.
>> Пусть в перле делают как хотят, а в с++ индексация связана с адрессной >> арифметикой и поэтому должна иметь аналогичное поведение. В адресной >> арифметике отрицательные индексы совершенно естественны (указатель может >> указывать на середину массива) kan>Кто сказал что связана? В stl не связана. Есть операция взятия элемента по индексу, индекс — неотрицательное число. Где kan>ты видел упоминание адресной арифметики в std::vector?
Ноги растут из языка. Если язык допускает отрицательный индекс для встроенного массива (которым в общем случае является указатель), значит контейнеры должны соотвествовать этому поведению, а не брать элементы с конца и т.п. К тому же кто сказал что контейнер должен быть обязательно от 0 до N? Если мне понадобится контейнер от -N до N то не думаю что меня будут мучать философские вопросы об отрицательных индексах и т.п.
>> kan>Индекс для вектора всегда "iter — vec.begin()" >> В отладчике это как посмотреть? Или условный брейкпоинт поставить? В >> общем случае никак.
kan>Ну это проблемы отладчика, а не stl. А вообще говоря можно: "iter._Myptr — vec._Myfirst" для MSVS7.1 с родным STL.
Да... юзабилити из этого решения так и прет
>> kan>В общем, какие-то надуманные проблемы, чаще всего возникающие от >> недопонимания понятия "итератор". >> Итераторы — это костыли и грабли под видом красивой идеи. Для массивов — >> лучше юзать индексы, для интрузивных node-based контейнеров достаточно >> знать указатель на элемент. Лишние сущности не нужны.
kan>Ты вообще понимаешь смысл понятия "generic programming"? Смысл абстракции? kan>Видишь ли, stl основывается на некоторых теоретических аспектах, а не только на общеизвестных практических приёмчиках.
Ты маркетолог или программист? Мне совершенно безразлично на чем основывается стл, главное то что она неюзабельная. Я пользователь библиотеки и меня интересуют в первую очередь удобство использования, отладки и другие прикладные аспекты, а не красивые идеи.
Здравствуйте, kan, Вы писали:
kan>Kluev wrote:
>> ЮБ>Ты себе представляешь stl без итераторов? >> Цель итераторов — сделать перебор элементов в разных контейнерах >> одинаковым. Это больная идея т.к. контейнеры должны перебиратся наиболее >> нативным для них способом. Как например должен выглядеть итератор для >> многомерных массивов, кольцевых списков и деревьев? Многие контейнеры в kan>Делается просто — несколько типов итераторов. Классический пример — begin-end/rbegin-rend. Для дерева можно 2 типа kan>сделать — обход в ширину и обход в глубину.
Не юзабельно. Т.к. рекурсия в данном случае проще и лучше.
kan>Т.е. функции, возвращающие итераторы могут быть даже с аргументами, например, задать по какой "кривой" обойти kan>многомерный массив.
Опять же неюзабельно. Во превых такой итератор прийдется писать, во вторых зачем? Если нужен какой-то путь в массиве, то отдельный массив с индексами то что нужно. Просто и в написании и в отладке.
>> концепцию итераторов просто не вписываются в результате чего программист >> вынужден пользоватся тем убогим набором который есть в stl. К томуже >> алгоритмы применимы не ко всем контейнерам и фактически единственным >> универсальным алгоритмом который подходит для всех является for_each. Но >> для этого существует более универсальный и могучий паттерн — визитор, >> который: >> >> 1) гораздо проще в реализации чем итератор >> 2) программы с его использованием получаются более элегантными kan>Требует задания функтора. Всегда. А это не всегда элегантно.
Не всегда. в контейнере просто заводятся функуци с визитором и без.
kan>Не позволяет иметь несколько итераторов, позволяющих обходить контейнер в разном порядке одновременно.
Для этого есть обычные циклы которые как раз для этих вещей и предназначены. Чтобы ходить как хочешь и куда хочешь.
>> Третье преимущество в том что внутри container.clear(clear_func) пербор >> идет способом родным для контейнера, что существенно упрощает отладку. kan>Какой способ является родным для дерева? Да даже для того же массива? Почему от начала к концу, а не наоборот? Или с kan>середины к краям?
Для дерева родной способ рекурсия, для массива и списка итерация. Иетартор всегда итерация, а значит написать итератор например для дерева может оказатся весьма непростой задачей. for_each не должен зависить от порядка элементов, а если требуются какие-то особые случаи то надо делать обычный цикл.
>> Так же итераторы в стиле stl совершенно не подходят для перебора >> кольцевых контейнеров. >> Т.к. в силу замкнутости их требуется итерировать не в диапазоне [begin, >> end), а в диапазоне [begin, last] kan>[begin, last] никак не позволяет задавать пустые интервалы, и никак это не обойти, и это является наиболее важным. Тем kan>более не надо делать замкнутый итератор единственно доступным для контейнера. Пусть это будет обычный std::list, просто kan>для него создать ещё один тип итератора, который будет замыкаться.
Это как? На каждой итерации проверять не в конце ли мы и если в конце то переходить на первый элемент? Значит ради идей уже пожертвовали удобством и отладкой, а теперь вот еще и на производительность плевать. Не самая лучшая идея. Более того такое решение все равно будет более неудобным чем специализированный контейнер.