Сравнение C++ Standard и Boost

Original author: Jeff Cogswell
  • Translation
Должно ли C++ сообщество придерживаться стандарта или отойти от него, чтобы создавать лучшие вещи с Boost?


Погодите, не та библиотека.

В марте 2011 года комитет ISO C++ утвердил финальную версию черновика новейшего стандарта C++. Языка, который официально был стандартизирован в августе того же года и стал известен как C++ 11. Теперь, по прошествии 2 лет, мы можем оглянуться назад и посмотреть на некоторые проблемы, затронувшие язык(аж с момента принятия первого международного стандарта в 1998 году) и сравнить его финальный вариант с популярной C++ библиотекой Boost.


С первой версией стандарта С++ включил официальную спецификацию библиотеки. До этого различные организации и частные лица создавали свои библиотеки, иногда основываясь на шаблонах и предоставляя различные типы контейнеров, таких как вектор или стек. Одной из таких библиотек, заслужившей внимание ANSI/ISO, была Стандартная Библиотека Шаблонов (Standard Template Library — STL), начатая парнем по имени Александр Степанов. Но на тот момент не существовало стандарта на сам язык, и различные поставщики компиляторов имели возможность трактовать его как вздумается. (Помню, как в середине 90-х была возможность «сбрасывать ссылки» с компилятором Borland C++, что сейчас невозможно с выходом стандарта).

Шаблоны были отельной проблемой, т.к. вендоры реализовывали их по-разному. Однако, появление STL заполнило важный пробел в C++, в частности, потребность в четко определённых и хорошо протестированных классах-контейнерах. Но, чтобы жизнь мёдом не казалась, вскоре появились различные реализации STL. Помню, как я использовал версию от HP(которая была одной из оригинальных), а моя компания в то же время выбрала версию от Silicon Graphics. В то же время существовала эта чуднАя библиотека от Rogue Wave Software, которая включала тонны дополнительных классов — некоторые на основе шаблонов, некоторые — нет; она была одна из самых популярных коммерческих библиотек того времени и отчасти напоминала STL. Важный момент: ни одни из этих библиотек не были взаимозаменяемыми. Нельзя было просто так взять, и заменить "#include" на другую библиотеку и ожидать, что всё будет работать, даже если предполагается, что используются те же самые классы и функции. Они просто не были совместимы. То, что 2 библиотеки реализовали класс «вектор», базируясь на оригинальной STL, не означало что он был одинаковым.

Хаос


Тогда в 1998 вышел стандарт, который включал собственную спецификацию библиотеки, названной Standard Library. Она во многом походила на STL и на самом деле базировалась на ней. Несмотря на то, что она была частью спецификации языка, Standard Library не являлось его частью в том смысле, что не была встроена в компилятор. Библиотека была спецификацией для набора классов, которые вендоры могли разрабатывать на C++ и поставлять вместе с компилятором. Это значило, что вы могли проигнорировать Standard Library если хотели, и вместо неё использовать какую-нибудь другую — что и делало большинство организаций, включая ту, в которой работал я, молодой инженер, создающий софт для телекоммуникационной индустрии. В этой компании программисты, работавшие до меня, использовали версию STL от Silicon Graphics, и потребовались огромные усилия, чтобы отойти от неё и заменить реализацией Standard Library. Конечно, в 1999 мы перешли на последние версии компиляторов и сделали множество изменений, после чего вендоры с гордостью объявили, что их компиляторы теперь соответствуют стандарту, что означало, что большая часть написанного нами кода не всегда компилировалась новыми компиляторами.

И так, разработчики ПО были вынуждены выбирать, когда начинали новые проекты: использовать Standard Library или проверенную STL, а если STL — то какую версию? Или остановиться на отличной коммерческой библиотеке вроде Rogue Wave? Они так же понимали — однажды выбрав одну из них, они будут вынуждены использовать её долгое время. Что же касается самой Standard Library, поставщики компиляторов часто создавали собственную её версию, которая не всегда с ней совпадала с оригиналом.

Это был полный бардак. И частично, проблемой был(и сейчас есть) сам процесс стандартизации. STL впервые был продемонстрировал Комитету по Стандартизации в 1993, но прошло 5 лет, прежде чем вышел стандарт. В промежутке не существовало даже стандарта на сам язык, не говоря уж про библиотеки, поэтому вендоры компиляторов получили не только свободу создавать библиотеки, но и сам язык. Примерно к 1996 году бардак достиг своего апогея. Borland включил множество новых фич в свою версию C++, но невозможно было просто скомпилировать их код другим компилятором. И если вы хотели использовать STL, вам нужно было найти ту версию, которая была портированна для вашего компилятора.

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

В этом процессе мало что изменилось. В 2006 году (через 3 года после небольшого обновления), казалось новый стандарт C++ должен стать реальностью. Он наконец вышел в 2011 году. Прошла куча времени.

Boost


В начале 2000-х новая библиотека набирает популярность — Boost.

Boost — это набор C++ библиотек, которые заполняют различные пробелы не только в Standard Library, но и в самом языке, двигая его вперёд. C++ вышел в 1983 году, после 4 лет разработки, и базировался на C, который вышел в 1972.

Давайте начистоту: несмотря на то, что я очень люблю C++ и даже до сих пор его использую(особенно для многоядерного программирования, которое является одним из моих фриланс-проектов), факт в том, что чем больше вы проводите времени с современными языками, такими как Java или Python, тем больше вы находите полезных фишек, встроенных непосредственно в язык — фишек, которые до сих пор не являются частью C++. Чёрт, тип string до сих пор не встроен в ядро языка, а реализован как шаблон в Standard Library. Язык сам по себе всё ещё базируется на символьных указателях и ссылках. Если вам нужен string — вы должны использовать одну из библиотек и часто звать метод c_str() класса string, чтобы получить указатель на символ, потому что string не встроен нативно в язык, не говоря уже о штуках типа list или dictionary. Хотите list в Python? Просто наберите «a = [1,2,3]». Готово.

Что же касается более продвинутых вещей, вроде лямбда-функций(которые так же нативно поддерживаются многими языками) — вот где библиотеки типа Boost будут полезны.

Конечно, фундаментальное отличие C++ в том, что он полностью компилируем. Но это аргумент, с которым можно спорить до тошноты, лучше и быстрее ли С++ современных динамических языков как с JIT компиляцией, так и без неё.

Библиотека Boost в конечном счёте служит нескольким целям: во первых, она предоставляет программистам как продвинутые вещи, такие как функциональное программирование, так и базовые, вроде смарт-указателей. Во-вторых, она является своего рода инкубатором для новых возможностей языка, которые могут стать стандартными.

Прямо на главной странице Boost замечено, что 10 библиотек из её состава было включено в стандарт C++ 11. Да, и она вышла в 2003 году — 10 лет назад, задолго до принятия стандарта.

Т.к. принятие стандарта 2011 года заняло так много времени, множество разработчиков начали использовать Boost(или совсем отказались от C++). Итак, давайте сравним Boost со Стандартом.

Стандарт 2011 и Boost


Стандарт C++ 11 включает несколько изменений в самом языке(например, приведён в порядок механизм компиляции шаблонов, что позволяет использовать «внешние шаблоны» («extern template»), а как же проще инициализировать объекты-контейнеры; и даже мехнизм определения типов(type inference mechanism)). Но так же стандарт содержит множество улучшений в Standard Library.

Тут есть важная оговорка, упомянутая ранее: несмотря на то, что стандарт включает описание Standard Library, он не включает имплементацию ни компилятора, ни языка. Всё это остаётся на совести третьих лиц, таких как поставщики компиляторов. Не существует эталонной реализации. Стандарт — это не код, это описание.

Так же, одна вещь, которую нужно знать о Boost — это что она на самом деле состоит из нескольких библиотек, и её дизайнеры намеренно раздели их. Это значит, что вы можете использовать только те библиотеки из Boost, которые вам нужны. Там есть несколько взаимозависимостей, но они хорошо документированы. Таким образом, вы можете комбинировать Boost со Standard Library, если необходимо. Например, старые добрые iostream являются частью Standard Library со времён первого стандарта 1998 года, и Boost легко работает с ними(но так же включает свои собственные iostream, если вам так удобнее).

Но Boost, на самом деле, гораздо больше, чем Standard Library. Она содержит около 175 классов, и текущая версия на SourceForge(1.53.0) занимает 93 Мб (включая документацию, а распакованные .hpp файлы в количестве 9000 штук занимают 90 Мб). Там есть классы, которые лично я нахожу невероятно полезными в моей работе, например те, что относятся к категории «эмуляция особенностей языка» («Language Features Emulation»). Например — конструкция foreach, спасибо механизму шаблонов в C++, позволяет писать циклы, которые выходят за рамки стандартных циклов C++, вот так:

int i = 0;
BOOST_FOREACH( i, container )
{
…
}

Хотя вы можете легко писать код, используя нативные циклы, есть несколько причин, почему вы можете захотеть писать их таким способом. Первая — кодирование становится чуточку проще. Вторая — джуниоры(или даже сеньоры, которые, как вы думаете, никогда не должны быть повышены) с меньшей вероятностью сделают всё ту же старую ошибку, написав что-то вроде "for (i=0; i<=length;i++)", а потом ещё будут спрашивать вас, почему этот код иногда работает, а иногда падает внутри функций, из которых был вызван.

Boost так же предоставляет огромный ассортимент могучих, но узко специализированных классов. Например, несколько лет назад мне понадобилась библиотека для работы с графами. В Boost есть такая. Нужен многомерный массив? Как насчёт контейнера, основанного на стеке? Boost всё это имеет. Но что мне больше всего нравится в ней — это целый набор хороших шаблонов программирования и полезная библиотека для работы с регулярными выражениями.

Но реализация Boost не очень проста, разработчикам пришлось наполнить библиотеку компиляторозависимым кодом. Например, реализация foreach конструкции включает такой код:

# if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
|| BOOST_WORKAROUND(__BORLANDC__, < 0×593)
|| (BOOST_WORKAROUND(BOOST_INTEL_CXX_VERSION, <= 700) && defined(_MSC_VER))
|| BOOST_WORKAROUND(__SUNPRO_CC, < 0×5100)
|| BOOST_WORKAROUND(__DECCXX_VER, <= 60590042)
# define BOOST_FOREACH_NO_RVALUE_DETECTION
# endif

Это хорошо для всех, использующих библиотеку — она будет работать с большинством распространённых компиляторов и полностью абстрагирует от их тонкостей.

Но теперь есть другой стандарт


Итак, Boost продолжал расти, но и над стандартом велась работа все годы, предшествовавшие 2011-ому. Он содержит некоторое хорошие классы, такие как собственных класс регулярных выражений, который имеет много общего с Boost-им аналогом.

Но что насчёт стандарта? Посмотрим на это с точки зрения вендоров. Стандартам C++ требовались годы, чтобы стать законченными. Стандарт 1998 был важен, потому что он устанавливал множество фундаментальных аспектов языка, таких как шаблоны или ссылки. Это сделало жизнь создателей библиотек гораздо проще. Но компиляторы всё ещё отличаются друг от друга, о чём свидетельствуют компиляторозависимые макросы в Boost. Частично, это вина вендоров, которые не буквально следовали стандарту, но так же из-за случайных неопреленностей в самих библиотеках.

В течении нескольких лет, предшествовавших стандарту 2011, в него так же было внесено несколько изменений. Поставщики компиляторов пытались привести свои компиляторы и реализации Standard Library, надеясь иметь уже стандартизированные продукты до того, как стандарт будет закончен. Но они стреляли по движущейся цели.

Давайте взглянем на 2 конкретных компилятора: GNU GCC и Microsoft's C++. Согласно этой таблице, текущая версия GCC 4.8 поддерживает почти все новые фичи, за исключением двух небольших (Rvalue ссылки для "*this" и «минимальная поддержка сбора мусора»). Но разработчики ясно дали понять, что это поддержка всё ещё в экспериментальном статусе.

Эта таблица от Microsoft показывает статус в Visual Studio 2012 — там множество «no» и «partial». Но обратите внимание на эту небольшую заметку на странице Microsoft, которая даёт представление о том, с чем она имели дело:

"После утверждения лямбда-функций в Рабочем Документе (версия 0.9) и изменяемых лямбд (mutable lambdas), добавленных в версию 1.0, Комитет по Стандартизации начал переделку. Это привело к появлению лямбд версии 1.1. Это произошло слишком поздно, чтобы быть включенным в Visual C++ 2010, но они есть в Visual Studio 2012."

Внимание здесь на самом языке. Но что насчёт Standard Library? Microsoft включилась в работу над тем, чтобы соответствовать стандарту, на самых ранних стадиях. Тем не менее, они пояснили, что некоторые вещи в этой библиотеки зависят от новых фич языка, которые они ещё не реализовали. И как результат — эти аспекты библиотеки так же не были реализованы. (Но они не указали, какие именно).

А вот GNU реализация Standard Library. Там много элементов, помеченных «N» и «Partial».


Сравнение



Прошло 10 лет, прежде чем начал формироваться стандарт и ушло 2 года на его утверждение. Множество изменений произошло за это время, и производители компиляторов не могут рисковать, реализовывая те из фич, которые могут быть потом отменены. Не очень приятная картина.

Boost, в то же время, неплохо продвинулся. Разработчики проделали отличную работу, убедившись, что их код работает с различными компиляторами. Boost зрела, хорошо оттестирована и неплохо работает. Процесс разработки продвигается вперёд без каких-либо препятствий со стороны стандартизационного комитета. Она продолжает развиваться.

А что насчёт изменений в Standard Library? Я уже упоминал библиотеку регулярных выражений, очень похожую и находящуюся под сильным влиянием Boost. Но учитывая тот факт, что Standard Library всё ещё не хватает реализации многих вещей из спецификации, имеет ли это значение? Помните, C++ стандарт — это не код, и не существует эталонной реализации. Стандарт просто говорит о том, что C++ компилятор должен делать, и что должно быть в реализации Standard Library. Если вы хотите использовать Standard Library, вам нужно использовать продукт третьих лиц, часто поставляемый вместе с компилятором, таким как GCC или версия Microsoft. И вот из чего вы на самом деле выбираете: GCC или Microsoft-ая реализация Standard Library или Boost, на основе которой Standard Library была смоделирована. И обе этих имплементации не являются полной, а одна из них называется «экспериментальной». Какую вы предпочтёте для использования в своих продуктах?

Комитету по Стандартизации потребовалось 8 лет чтобы разобраться, что же всё-таки должно быть в стандарте, и производителям компиляторов пришлось бы ждать, пока всё будет улажено, прежде чем публиковать свою версию Standard Library. Вместо этого они публиковали куски, которые были в лучшем случае альфа-версиями. И они до сих пор не закончили. В то же время настоящее C++ сообщество движется дальше, создавая лучшие вещи, такие как Boost.

Через несколько лет появится новый стандарт. Мы это уже проходили. К тому времени Boost станет ещё лучше.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 76

    +24
    Чёрт, тип string до сих пор не встроен в ядро языка, а реализован как шаблон в Standard Library.
    Если автору нужно, чтобы было как в Python, пусть программирует на Python. Не надо превращать C++ в еще один C#.
    Все это нытье про необходимоть добавить в C++ рефлекшн и сборку мусора вызывает беспокойство. На это почему-то не обращают внимание, но в C++11 стандартная библиотека теперь местами интегрируется с языком. Например, лямбда в общем случае может иметь тип только std::function, что лишает разработчика возможности создать альтернативу, а язык его гибкости и прозрачности, одного из мощных достоинств.
    Статья не понравилась, какой-то плохо структурированный поток мыслей. Или перевод такой.
      +11
      А по-моему, как раз насчет строк автор прав. Мощность языка это, конечно, клёво, но когда каждая 2я библиотека имеет свою реализацию строк и вообще всего на свете — это печально. И уж точно не увеличивает совместимость библиотек между собой, простоту и доступность языка для новых разработчиков.

      Конечно, некоторые фичи в плюсах смотрелись бы глупо, но отрицать очевидные недостатки языка тоже не стоит.
        +11
        Я не совсем понимаю, в чём конкретно он прав? Класс std::string является стандартным, и в этом смысле он ничуть не менее стандартен, чем строки в Питоне или C#. Кто-то реализует альтернативы, но обычно у этих реализаций уши растут из девяностых, когда стандарта действительно не было, но с этим ничего не сделаешь. Если завтра в C++ введут ключевое слово string без префикса std::, ничего не изменится, и альтернативные реализации никуда не денутся.

        А про всё на свете — это, к сожалению, плата за стандарт ISO, который принимается медленно. Скажем, стандартная многопоточность только сейчас появилась, хотя Страуструп давно об этом говорил. Если бы у него была власть ван Россума, эту проблему решили бы десять лет тому назад.
          0
          Я думаю, тут проблема в бинарной совместимости.
          Если язык managed или использует VM, string из разных модулей runtime-среда приведёт к одному типу.
          Но для C/C++ на уровне ABI стандартизирован только обмен классами с описанными virtual функциями, без доступа к полям (например, как в COM), а это громоздко.

          Я видел небрежно спроектированную программу, которая подключает плагины с использованием string в интерфейсе (скомпиленную с STLPort 4). В какой-то версии произошло обновление до STLPort 5, все плагины слетели.
            +1
            Понимаю. Наверно, это потенциальная проблема любого компилируемого в объектный код языка.
          +3
          когда каждая 2я библиотека имеет свою реализацию строк и вообще всего на свете — это печально
          Кому нужно решение по умолчанию, используют std::string. Лишать остальных права выбора и контроля над своей программой на базовом уровне не вижу оснований. В этом весь дух C++, убрать его, значит убить язык. Наверное, некоторые именно этого и добиваются.

          простоту и доступность языка для новых разработчиков
          Мне неочевидна истинность утверждения, что для новых разработчиков C++ должен быть во всем простым и доступным.
            +3
            У std::string всё-таки есть сложности с юникодом, в таких случаях проще использовать QString, которая просто нормально работает, чем изучать особенности кодирования юникода в конкретной платформе.
              0
              Ну, сейчас уже есть стандартные библиотечные средства перевода, так что эта проблема тоже по идее решена.
                +2
                Всё-таки в Qt они удобнее :) Да и QtCore собирается нынче для всего на свете и он не такой толстый, как boost, хотя жаль, что они свою библиотеку контейнеров не сделали отдельным модулем, чтобы тащить только её, но не тащить всякие QObject штуки.
                  +1
                  Ну с этим я не спорю :) Но если в эту сторону рассуждать, про любой язык программирования можно сказать, что то или иное стандартное средство в нём менее удобно сделано, чем сторонняя библиотека.
          +4
          Включение строк в язык сильно противоречит идеологии нулевой стоимости, которая гласит, что не используемая вами фича должна вам обходиться бесплатно. Лямбда разве не анонимный функтор?
            +2
            Cогласен, С++ дает возможность делать то, что именно нужно, а все дополнительные фишки должны быть дополнительными.
              –1
              C++ дает слишком много возможностей :)
                +1
                Да вы что, я же любя
                  –1
                  Вы так говорите, будто это что-то плохое.
                +2
                лямбда в общем случае может иметь тип только std::function

                Что? Вы явно что-то неправильно поняли про лямбды. Никто не мешает завернуть лямбду в std::function, но сама лямбда имеет другой тип — «unique unnamed nonunion class type», как сказано в стандарте.
                  +2
                  Ниже уже написал, что ошибался. Отредактировать исходное сообщение не могу, к сожалению. Прошу прощения.
                  Просто когда некоторое время назад изучал этот вопрос, или действительно что-то неправильно понял, или используемый на тот момент компилятор был недостаточно функционален, но почему-то запомнился именно такой вывод. Сейчас проверил, действительно все работает, как надо.
                  Остается еще std::initializer_list, как тут заметили. Ну и вообще я про намечающуюся тенденцию, которая лично меня настораживает.
                    +1
                    Ну да. Еще пример можно привести из той же оперы: оператор typeid возвращает ссылку на std::type_info.
                      +1
                      А чем вызвана ваша обеспокоенность в случае с std::initializer_list и std::type_info?

                      Использование std::initializer_list не обязательно, более того, чтобы его использовать, нужно явно сказать компилятору. Он не имеет overhead'а. Написать библиотечный класс с аналогичной функциональностью без вмешательства в работу компилятора невозможно. В этом принципиальное отличие std::initializer_list от std::vector/std::string, которые можно реализовать без изменения компилятора.
                        0
                        std::initializer_list без проблем реализуется без изменения компилятора. пруф чуть ниже.
                          +1
                          Всё-таки отличия и проблемы есть:
                          1. Копирование std::initializer_list не приводит к копированию элементов
                          2. N виртуальных вызовов на инициализацию массива из N элементов это слишком. Лучше уж взять boost::assign, хотя у него синтаксис похуже
                          3. std::initializer_list поддерживает begin/end, что позволяет использовать его в range-base-for
                        0
                        Остается еще std::initializer_list, как тут заметили.

                        ответ тут
                    +1
                    Например, лямбда в общем случае может иметь тип только std::function, что лишает разработчика возможности создать альтернативу

                    на самом деле лямбда это просто класc с переопределённым оператором (), так что если реализовывать свой делегат, то проблем это не вызовет.
                      0
                      на самом деле лямбда это просто класc с переопределённым оператором ()
                      Подразумевалась альтернатива std::function, как элементу пусть и стандартной, но библиотеки, а не лямбде, которая как раз удобный синтаксический сахар языка.
                        +2
                        Подразумевалась альтернатива std::function

                        ну так я про неё и говорю, написать свою альтернативу, которая принимает лямбду не проблема
                          0
                          Можно пример?
                            0
                            Как раз обходил проблему кривого std::function в gcc 4.4 и накатал простенького делегата
                            github.com/gorthauer/vreen/blob/master/src/api/reply.h
                              –1
                              Вы о чем?
                              Пока не вижу примера, как поймать лямбду без использования std::function, зато вижу минус на просьбу продемонстрировать, что "написать свою альтернативу, которая принимает лямбду не проблема".
                                0
                                Этот код ловит лямбду.
                              +3
                                0
                                Благодарю, был неправ.
                                0
                                auto hello = [] () {  std::cout << "Hello, world!" << std::endl; };
                                auto hello_100 = [hello] () { for (int i = 0; i < 100; ++i) hello(); };
                                
                                  0
                                  Это не то, пример ничего не говорит про суть типа hello, неявно он вполне мог бы быть и std::function.
                                  Вот здесь достаточный пример уже был приведен.
                                    0
                                    Не может он быть std::function. Между
                                    auto hello = [] () { std::cout << «Hello, world!» << std::endl; };
                                    и
                                    std::function<void(void)> hello = [] () { std::cout << «Hello, world!» << std::endl; };
                                    есть большая разница.
                                    Std::function использует malloc и указатели для полиморфизма в рантайме, а лямбда — это просто анонимная структура с перегруженным (), ваш пример с auto никуда в кучу не лезет.
                                      0
                                      Std::function использует malloc и указатели для полиморфизма в рантайме, а лямбда — это просто анонимная структура с перегруженным (), ваш пример с auto никуда в кучу не лезет.
                                      Все это следует из текста примера?
                                      Напомню, что речь была про демонстрацию возможности захватить лямбду собственной структурой данных.
                            0
                            А std::initializer_list?
                              0
                              А вот насчёт этого, без понятия, ещё не ковырял, но наверняка это всё также оборачивается в какой-то неопределённый класс + шаблонная магия ))
                                0
                                Намного посидел вечером и решил разобраться со списками инициализации, в общем std ненужен, вот пруф
                                liveworkspace
                                  0
                                  Работает не так, как initializer_list: http://liveworkspace.org/code/3JlXXR$0
                                  Элементы в памяти расположены в обратном порядке, как параметры в стеке.
                                    0
                                    + ещё список проблем: http://habrahabr.ru/post/173639/#comment_6035941
                                      0
                                      Элементы в памяти расположены в обратном порядке, как параметры в стеке.

                                      Я просто привёл простейшее решение, можно всё сделать совершенно по другому, просто хотел показать, что чтобы пройтись по элементам std::initializer_list ненужен, и можно написать свой аналог, также как и в случае с std::function.
                                      + ещё список проблем:

                                      То что я привёл, ни в коем случае не претендует на эталонную реализацию, или на реализацию которая имела бы полный список возможностей std::initializer_list. Как я уже сказал просто хотелось продемонстрировать что и без std::initializer_list можно использовать списки.
                                        0
                                        Я просто привёл простейшее решение, можно всё сделать совершенно по другому, просто хотел показать, что чтобы пройтись по элементам std::initializer_list ненужен, и можно написать свой аналог
                                        Чтобы данный (модифицированный) пример корректно работал, нужно скопировать каждый элемент внутрь списка, т.е. это перебор отдельных копий, а не элементов исходного списка, представленных в виде непрерывного массива. Строго говоря, принципа заменяемости initializer_list пример пока не демонстрирует.
                                          0
                                          Ок вот другой вариант, ещё лучше и проще. Тут копируются только указатели.
                                          liveworkspace
                                          (я таких могу ещё кучу придумать)
                                            0
                                            Ок вот другой вариант, ещё лучше и проще.
                                            Принципиальной разницы не появилось, параметры все так же на стеке в обратном порядке, о доступе к массиву элементов речи нет.

                                            Тут копируются только указатели.
                                            Указатели на параметры в стеке, которые недействительны после выхода из конструктора списка?

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

                                              Так я и не делаю аналог initializer_list, я хочу показать что и без него можно пройтись по списку и получить то что нужно.
                                              Указатели на параметры в стеке, которые недействительны после выхода из конструктора списка?
                                              угу но, зато можно по нему потом пройтись как по массиву без проблем и скопировать данные куда угодно.
                                              Зачем куча, если нужен один, воспроизводящий поведение initializer_list.

                                              ещё бы знать как он внутри устроен, я же на угад всё делал.
                            • UFO just landed and posted this here
                                +6
                                с меньшей вероятностью сделают всё ту же старую ошибку, написав что-то вроде «for (i=0; i<=length;i++)», а потом ещё будут спрашивать вас, почему этот код иногда работает, а иногда падает внутри функций, из которых был вызван

                                Имеется ввиду запись во вне аллоцированной памяти? или есть еще какая-то причина поведению: «почему этот код иногда работает, а иногда падает внутри функций»?
                                  0
                                  Лучше объясните, почему в C++ до сих пор нет выборочной инициализации структур и массивов как в C99?

                                  struct structure {
                                  int a;
                                  int b;
                                  };

                                  enum {
                                  ITEM_A,
                                  ITEM_B,
                                  NUM_ITEMS
                                  };

                                  static const struct structure item_a = {
                                  .a = 1,
                                  };

                                  static const struct structure item_b = {
                                  .b = 2,
                                  };

                                  static const struct structure* const array[NUM_ITEMS] = {
                                  [ITEM_A] = item_a,
                                  [ITEM_B] = item_b,
                                  };
                                    +1
                                    Потому что такое поведение без последствий можно реализовать только для POD-типов. Но тогда уж лучше этого не вводить и оставить язык консистентным. Конструкторы и списки инициализации спасают во многих случаях.
                                    +2
                                    Я не понимаю зачем сравнивать языки с управлением памятью и C++. Это как сравнивать лопату и утюг. И то и то инструмент, но для разных вещей.

                                    C++11 привнес в язык очень много чего полезного и нужного. Сейчас даже не представлю как писать на C++03. Очень жаль, что так поздно, язык потерял много сторонников.

                                    На счёт libstdc++, там из действительно нужного остаются нереализованными регэкспы и emplace для некоторых контейнеров.
                                      +1
                                      Да, но все равно мне libc++ больше нравится, а она в общем-то и с gcc вроде бы работает :)
                                        –2
                                        А с portability что? Сильно приходится менять при компиляции gcc. Я вот всё думаю на clang переползти, но пока по скорости он проигрывает.
                                          0
                                          Я ни строчки не поменял :) Но я не слишком активно юзаю stl, boost же собирается с это библиотекой.
                                          clang я использую для debug сборок, скорость не так критична, а вот диагностика уже на первый план выходит.
                                            –1
                                            Это хорошо, у меня какие-то ворнинги выдавал вроде.

                                            Ну в бусте полно #ifdef #else чем забитвать свой код не охота совершенно.
                                    • UFO just landed and posted this here
                                        +4
                                        При таком числе тянущих на себя одеяло это сложно.
                                        +9
                                        Я люблю C++, а C++11 еще больше, и использование Boost-а тоже приветствую, но статья, извините, просто ни о чем.
                                        Пустой треп про стандарты и комитеты. Не увидел никакого сравнения.
                                          +4
                                          Статья сравнивает тёплое с мягким. Язык-языком, стандартная библиотека — ок, буст — тоже ок. С чего бы их сравнивать? Выглядит как попытка защитить буст. Как-будто его зачем-то надо защищать. В общем, странный текст.
                                            +5
                                            Хотите list в Python? Просто наберите «a = [1,2,3]». Готово.

                                            Как это мило — сравнивать интерпретируемый язык с динамической типизацией с компилируемым языком со строгой типизацией.

                                            std::vector<int> a = {1,2,3}; //Готово.
                                            

                                              +1
                                              В C++ слабая типизация (нестрогая):
                                              C++ — Статическая | Слабая | Явная


                                              Наверное, подразумевалась статическая или явная типизация.
                                                0
                                                Да, подразумевалась явная типизация. Спасибо за ликбез.
                                                0
                                                Это же вроде как совсем недавно появилось?
                                              • UFO just landed and posted this here
                                                  –2
                                                  Меня QT немного пугает необходимостью использования дополнительного препроцессора.
                                                    +1
                                                    Если вы не используете специфических вещей, типа сигналов и слотов, использование препроцессора необязательно.
                                                      +2
                                                      Это не препроцессор, а кодогенератор, исходники там стопроцентный С++. И вообще, у них был в планах проект по написанию плагина для clang'а вместо юзания moc'а, но пока дальше слов дело не пошло.
                                                      • UFO just landed and posted this here
                                                        0
                                                        Это вы просто не сталкивались, наверное, с побочными эффектами COW типа непотокобезопасности контейнеров при чтении и обязталеьной возможностью скомпилировать detach, с недоделанными умными указателями и с другими интересными вещами.
                                                          0
                                                          По-моему, a priori предполагать потокобезопасность контейнеров вообще никогда не стоит
                                                            +1
                                                            На чтение контейеры обычно потокобезопасны. Вот, например, что говорит MSDN.

                                                            Qt тоже утверждает, что контейнеры потокобезопасны на чтение.

                                                            Вот только проблема в том, что взятие итератора от контейнера — это уже запись с точки зрения Qt. При взятии итератора будет вызван detach, который не потокобезопасен:

                                                            QVector<int> v;
                                                            QVector<int>::const_iterator it = v.begin(); // does no thread safe
                                                            QVector<int>::const_iterator it2 = v.constBegin(); // DOES thread safe
                                                            

                                                        +1
                                                        Вторая — джуниоры(или даже сеньоры, которые, как вы думаете, никогда не должны быть повышены) с меньшей вероятностью сделают всё ту же старую ошибку, написав что-то вроде «for (i=0; i<=length;i++)», а потом ещё будут спрашивать вас, почему этот код иногда работает, а иногда падает внутри функций, из которых был вызван.


                                                        Объясните, пожалуйста, что имелось ввиду здесь под старой ошибкой?
                                                        В чём она?

                                                        UPD:
                                                        Всё понял…
                                                        i должно быть строго меньше length.
                                                        Прошу прощения
                                                          0
                                                          Может я гоню жестоко, но почему это
                                                          i должно быть строго меньше length.
                                                          О____О
                                                            0
                                                            Потому что в массиве длиной 10 последний элемент будет под номером 9.(Нумерация начинается с 0)
                                                              0
                                                              Про массив в тексте вообще ни слова не было. Этот «сферический цикл в вакууме», не предполагающий что итерация происходит по массиву.
                                                                0
                                                                В статье речь шла про конструкцию foreach, которая используется для итерации по контейнерам. И приведен пример, что мы могли бы использовать нативный вариант с циклом. Поэтому для контейнеров, которые предполагают индексацию, такая итерация, скорее всего будет ошибочной.
                                                                В «сферическом цикле в вакууме» искать ошибки вообще нет смысла. Поэтому необходим контекст, в котором такая конструкция может привести к ошибке.
                                                                Я для себя его определил как итерация по коллекции.
                                                                  0
                                                                  Логично! Теперь вижу, что, действительно, подразумевался контейнер!

                                                        Only users with full accounts can post comments. Log in, please.