The std::vector<bool> specialization does not conform to the requirements of a container and does not work as expected in all STL algorithms. In particular &v[0] does not return a contiguous array of elements as it does for other vector types. Additionally, the C++ language standard guarantees that different elements of an STL container can safely be modified concurrently, except for a container of std::vector<bool> type.
Здравствуйте, rg45, Вы писали:
R>Как я полагаю, просто для упрощения текста примеров. Для учебных примеров такие вопросы оптимизации не являются существенными. Тем более с учетом того, что многие реализации std::string используют технологию "copy on write". Хотя эффект получается прямо противоположный ожидаемому — вместо упрощения у читателей появляются вопросы, вполне закономерные.
Последние требования к интерфейсу std::basic_string фактически запрещают идиому "copy on write", так что начиная с C++11 строки фактически обязаны копироваться. Для строк осталась только small strings optimization, по желанию.
Здравствуйте, Videoman, Вы писали:
BFE>>Есть. Есть распространённый пример: std::vector<bool> для которого ссылки не компилируются: BFE>>... V>Можно не рассматривать, т.к. std::vector<bool> не рекомендуется использовать, именно из-за его кривого дизайна.
Интересно. Раньше его не рекомендовали использовать, из-за реализации, а теперь из-за дизайна.
L>В цикле for вместо int часто указывают auto, тогда компилятор сам определяет тип переменной t. Но оказывается, вместо int можно также указать ссылку int& — у кого-то я видел такое, и в моем примере это нормально работает. Для надежности я указал const int&, и у меня в данном примере это тоже нормально работает. L>Все же, как лучше делать, в каких случаях и почему?
Здравствуйте, B0FEE664, Вы писали:
BFE>>>Есть. Есть распространённый пример: std::vector<bool> для которого ссылки не компилируются: BFE>>>... V>>Можно не рассматривать, т.к. std::vector<bool> не рекомендуется использовать, именно из-за его кривого дизайна. BFE>Интересно. Раньше его не рекомендовали использовать, из-за реализации, а теперь из-за дизайна.
В данном случае речь об одном и том же.
Какой-то криворукий эконом вместо выделения отдельного bit_vector<> испортил стандартный vector.
В сочетании с "мы говорим vector — подразумеваем массив, говорим array — подразумеваем vector" это выглядит особенно впечатляюще.
а я и не шутил. Программист образца 98 года конечно ставил const & и больше не думал. Но после 11-го года программист задаёт себе извечный вопрос: а можно ли (и надо ли) мне будет перемещать элементы контейнера. Ну для меня решить этот вопрос не так то и легко. Надо сразу взвесить много факторов. И зачастую точно ответить на вопрос иногда не получается. Тогда приходит на помощь auto&&. Я так пишу и даже не останавливаюсь. Откажусь я от перемещения, или, наоборот, буду перемещать: мне уже всё равно — шапка цикла останется неизменной.
Здравствуйте, sergii.p, Вы писали:
SP>а я и не шутил. Программист образца 98 года конечно ставил const & и больше не думал. Но после 11-го года программист задаёт себе извечный вопрос: а можно ли (и надо ли) мне будет перемещать элементы контейнера. Ну для меня решить этот вопрос не так то и легко. Надо сразу взвесить много факторов. И зачастую точно ответить на вопрос иногда не получается. Тогда приходит на помощь auto&&. Я так пишу и даже не останавливаюсь. Откажусь я от перемещения, или, наоборот, буду перемещать: мне уже всё равно — шапка цикла останется неизменной.
А вот здесь тебя ждет засада: переменная цикла, объявленная как auto&& в любом случае будет lvalue ссылкой (если говорить об использовании со стандарными контейнерами), несмотря на значок "&&". Она может оказаться константной ссылкой, а может оказаться неконстантной — в зависимости от константности/неконстантности самого контейнера. Но, в любом случае, это будет lvalue ссылка. Так что перемещать элементы конейнера без явного использования std::move (или развноценного преобразования) тебе все равно не удастся.
И даже в гипотетическом случае (какие-то нестандартные контейнеры), если эта переменная цикла каким-то образом окажется настоящей rvalue ссылкой, выражение, составленное из имени этой переменной, все равно будет являться lvalue выражением и также потребует явного преобразования для перемещения:
Здравствуйте, sergii.p, Вы писали:
SP> Но после 11-го года программист задаёт себе извечный вопрос: а можно ли (и надо ли) мне будет перемещать элементы контейнера. Ну для меня решить этот вопрос не так то и легко. Надо сразу взвесить много факторов. И зачастую точно ответить на вопрос иногда не получается. Тогда приходит на помощь auto&&. Я так пишу и даже не останавливаюсь. Откажусь я от перемещения, или, наоборот, буду перемещать: мне уже всё равно — шапка цикла останется неизменной.
Вот это и плохо.
Если цикл принципиально предназначен не менять элементы — лучше это обозначить в шапке, чтобы 1) было видно по этому циклу, 2) случайное изменение внутри — ломалось и требовало правки формы цикла.
Здравствуйте, netch80, Вы писали:
BFE>>>>Есть. Есть распространённый пример: std::vector<bool> для которого ссылки не компилируются: BFE>>>>... V>>>Можно не рассматривать, т.к. std::vector<bool> не рекомендуется использовать, именно из-за его кривого дизайна. BFE>>Интересно. Раньше его не рекомендовали использовать, из-за реализации, а теперь из-за дизайна.
N>В данном случае речь об одном и том же.
Раньше были реализации, где каждый элемент массива std::vector<bool> занимал от одного до 4-х байт.
N>Какой-то криворукий эконом вместо выделения отдельного bit_vector<> испортил стандартный vector.
Пора разрабатывать std::v2:: ...
N>В сочетании с "мы говорим vector — подразумеваем массив, говорим array — подразумеваем vector" это выглядит особенно впечатляюще.
array — это std::array
Здравствуйте, B0FEE664, Вы писали:
N>>В данном случае речь об одном и том же. BFE>Раньше были реализации, где каждый элемент массива std::vector<bool> занимал от одного до 4-х байт.
Ну если естественный bool занимает "от одного до 4-х байт", то так и должно быть в несжатом хранилище. А сжатое должно идти отдельным пунктом.
N>>Какой-то криворукий эконом вместо выделения отдельного bit_vector<> испортил стандартный vector. BFE>Пора разрабатывать std::v2:: ...
Ага, и допускать 15 лет на адаптацию под него
N>>В сочетании с "мы говорим vector — подразумеваем массив, говорим array — подразумеваем vector" это выглядит особенно впечатляюще. BFE>array — это std::array
Я про то, что — цитирую Страуса —
One could argue that valarray should have been called vector because it is a traditional mathematical vector and that vector (§16.3) should have been called array. However, this is not the way the terminology evolved. A valarray is a vector optimized for numeric computation, a vector is a flexible container designed for holding and manipulating objects of a wide variety of types, and an
array is a low-level, built-in type.
"Извините, мы тут напутали в терминологии, живите теперь с этим".
Здравствуйте, rg45, Вы писали:
R>В таком случае я не понял, как объявление переменной цикла как auto&&, может помочь решить извечный вопрос
да, может не очень хорошо объяснил. Короче, идея в том, что при написании шапки цикла я вообще отключаю мозги
for(auto&& el: array) { }
меня не интересует, собираюсь ли я менять элементы или перемещать их, либо мне нужны только константные методы. Ответы на эти вопросы будут позже. Короче, универсальная ссылка на то и универсальная, чтобы съесть всё. И этот подход облегчает задачи рефакторинга. Потому что часть кода (шапка цикла) работает в любом случае независимо от внешней обстановки.
Здравствуйте, rg45, Вы писали:
R>В связи с этим уместно будет, пожалуй, вспомнить о не менее распространенной рекоммендации никогда не использовать vector<bool>. Как, например, здесь:
Дурацкая рекомендация. Нам нога не нужна — и вы свою не используйте.
Ежу понятно, что раз минимальная адресуемая железом единица — байт, то к битам конкурентно ходить не получится. Для конкурентности юзайте байт. Это ограничение железа.
А булев массив признака простоты на числа до 2^32 займёт 256 метров при нынешней, нормальной реализации, и 2 гига при байтовой, всем пофигу?
Нахрена мне той дурацкой конструкцией получать "непрерывный массив" бит — тем более не ясно. У меня сам vector<bool> уже фактически таковым является.
Похоже, программисты на бейсике и питоне взялись писать рекомендации плюсовикам. Ну, удачи им.
Здравствуйте, cures, Вы писали:
R>>В связи с этим уместно будет, пожалуй, вспомнить о не менее распространенной рекоммендации никогда не использовать vector<bool>. Как, например, здесь: C>Дурацкая рекомендация. Нам нога не нужна — и вы свою не используйте.
Проблема в том, что можно изменить где-то в другом месте тип foo_t на тип bar_t, до этого независимо изменить bar_t, например, с int на bool, а от этого изменится реализация vector<bar_t>.
Именно поэтому надо было не переопределять vector<bool>, а делать отдельный тип, или шаблонный контейнер плотной упаковки типа bit_vector<N> для элементов по N бит.
C>Ежу понятно, что раз минимальная адресуемая железом единица — байт, то к битам конкурентно ходить не получится. Для конкурентности юзайте байт. Это ограничение железа.
1. Ежу непонятно, потому что логический переход bool -> бит изначально не был предусмотрен.
2. Вообще-то такого ограничения нет. X86: lock cmpxchg, lock bts, и так далее. Но к этому надо готовиться явно, а не ломать vector.
C>А булев массив признака простоты на числа до 2^32 займёт 256 метров при нынешней, нормальной реализации, и 2 гига при байтовой, всем пофигу?
Задача массива признака простоты в таком виде, ещё и конкурентно... это кому ж такое постоянно нужно, и почему от этого должны страдать остальные пользователи C++?
C>Нахрена мне той дурацкой конструкцией получать "непрерывный массив" бит — тем более не ясно. У меня сам vector<bool> уже фактически таковым является. C>Похоже, программисты на бейсике и питоне взялись писать рекомендации плюсовикам. Ну, удачи им.
Вполне себе на плюсах. Но это не означает, что инструмент — неприкосновенная корова.
Здравствуйте, cures, Вы писали:
R>>В связи с этим уместно будет, пожалуй, вспомнить о не менее распространенной рекоммендации никогда не использовать vector<bool>. Как, например, здесь:
C>Дурацкая рекомендация. Нам нога не нужна — и вы свою не используйте.
Ну хорошо, давай сейчас не будем об этом спорить. Все-таки, сейчас в фокусе внимания несколько другие вещи. А исключения, как известно, только подтверждают правила.
Здравствуйте, sergii.p, Вы писали:
SP>да, может не очень хорошо объяснил. Короче, идея в том, что при написании шапки цикла я вообще отключаю мозги
Отключать мозги при программировании, а особенно на С++, не лучший подход. Упрощая себе работу при написании вы сильно усложняете ее тому кто будет читать ваш код и не раз, особенно, если после ":" в цикле стоит функция и сразу не понятно какой тип контейнера она возвращает и какой тип элементов этого контейнера. Auto вводился, в основном, для упрощения работы с "трехэтажными" шаблонами, для случая где понятно или не важно какой используется тип. Злоупотребляя auto вы лишаете себя и компилятор дополнительной возможности контроля на уровне типов. Из этой же серии: я отключаю голову и везде не пишу const, ни при объявлении переменных, ни при объявлении методов класса, и все работает!
Здравствуйте, netch80, Вы писали:
N>Проблема в том, что можно изменить где-то в другом месте тип foo_t на тип bar_t, до этого независимо изменить bar_t, например, с int на bool, а от этого изменится реализация vector<bar_t>.
Так именно от этого сценария указанная рекомендация никак не спасает. Даже если ей следовали, и сознательно нигде не пользовались вектором булов, а потом не со зла заменили где-то инт на бул, внезапно, тихо и незаметно оный вектор всё равно оказывается в проекте. И, может быть, летят тонны варнингов с еррорами, если кто-то пользовался той фигнёй, либо просто выдаётся фигня, если кто-то тупо полез менять значения конкурентно.
Тогда уж надо рекомендовать вообще не пользоваться вектором, но идиотизм подобной рекомендации оказался очевидным даже для авторов.
N>Именно поэтому надо было не переопределять vector<bool>, а делать отдельный тип, или шаблонный контейнер плотной упаковки типа bit_vector<N> для элементов по N бит.
Полностью с Вами согласен. И ещё хорошо бы добавить туда приятных (быстрых) операций типа найти следующий нулевой (ненулевой) бит, поставить, очистить или свопнуть биты с такого-то по такой-то, возможно, ещё какие-то. Из-за их отсутствия иногда приходится самому имплементить.
Беда этой рекомендации не в том, что она даёт нам совет по написанию стандартной библиотеки. Тогда бы мы её просто проигнорировали, ибо мы ту библиотеку не пишем. Она просто требует, чтобы мы не пользовались тем, что есть, при том, что другого приемлемого для этих целей нет.
Единственная осмысленная надежда, которая могла двигать такими советчиками — что все их послушаются, перестанут пользоваться вектором булов, Страуструп и Степанов это увидят, заплачут и всё переделают. В этом я и пожелал им удачков.
Таким образом, данная рекомендация никак не помогает, когда нужно, и вредит, когда не нужно. Поэтому я и назвал её дурацкой.
И ещё, не надо темплэйтить его по N, это тогда получится array. От вектора мне таки может понадобиться изменение размера, либо использование с размером, известным только на рантайме.
Идея вектора слишком хороша, чтобы её так просто выбрасывать.
C>>Ежу понятно, что раз минимальная адресуемая железом единица — байт, то к битам конкурентно ходить не получится. Для конкурентности юзайте байт. Это ограничение железа.
N>1. Ежу непонятно, потому что логический переход bool -> бит изначально не был предусмотрен.
Не понял утверждения. bool — тип для хранения логического значения, бит — единица информации, как раз под одно логическое значение. Кем и когда не была предусмотрена их связь?
N>2. Вообще-то такого ограничения нет. X86: lock cmpxchg, lock bts, и так далее. Но к этому надо готовиться явно, а не ломать vector.
Если они будут медленнее, чем в простой нынешней реализации, то это надо таки выносить в другой класс. Мы не должны платить за то, чем не пользуемся.
N>Задача массива признака простоты в таком виде, ещё и конкурентно... это кому ж такое постоянно нужно, и почему от этого должны страдать остальные пользователи C++?
В том-то и дело, что конкурентно как раз мне не нужно. Максимум оттуда будут параллельно читать, что никому не повредит. А мне запрещают ковыряться в носу этим пользоваться, ссылаясь на мифический конкурентный доступ и проблемы при нём.
Ну и это всего лишь один из первых вспомнившихся примеров, мало ли когда нужно много булов. А вот пользователи питона, которым пофигу, сколько что занимает памяти, полезшие зачем-то в плюсы, должны страдать.
N>Вполне себе на плюсах. Но это не означает, что инструмент — неприкосновенная корова.
Инструмент есть, переделывать его никто не собирается. Авторы инструмента, очевидно, послали этих деятелей довольно далеко, и, может быть, ещё как-то грубо с ними обошлись. Теперь эти советчики просто пакостят авторам, отговаривая пользователей этим инструментом пользоваться?
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, B0FEE664, Вы писали:
BFE>>Есть. Есть распространённый пример: std::vector<bool> для которого ссылки не компилируются:
R>Ну да, это я выпустил из виду. Ну, на этом и все, пожалуй
R>В связи с этим уместно будет, пожалуй, вспомнить о не менее распространенной рекоммендации никогда не использовать vector<bool>. Как, например, здесь:
vector<bool> это, как раз, пример когда специализация сужает область применения обобщённой сущности, что есть нонсенс, а значит ошибка архитектуры. Если к специализации нужно писать отдельную рекомендацию по применению, то от такой специализации нужно отказаться. Как здесь правильно сказали следует использовать отдельный тип.