LINUX.ORG.RU

юнионы в C++

 


2

4

Пишу телегу против плюсов. В связи с этим вопрос - насколько широко в плюсах используются нуль-терминированные строки, юнионы, неумные указатели и всё такое плохое, что делает Си опасным языком.

Даже интересует не столько то, насколько они используются в существующих программах, а есть ли примеры программ, где хорошие средства плюсов сконсолидировались и поставили заслон от опасных конструкций Си, позволив полностью избежать их использования и избавиться от типичных ошибок Си. Можно ли так написать что-то существенно сложное? Сделано ли это в любимых библиотеках (Буст, QT и иже с ними)? Вторая часть вопроса - это неопределённое поведение. В Си его много. Это подаётся как фича, но с точки зрения безопасности это дыра. Меньше ли неопределённого поведения в С++?

Есть две полярные точки зрения на вопрос:

а) С++ перекрывает Си, поэтому там всё сделано по-другому, поэтому безопасность выше б) С++ - наследник Си и в целом наследует его недостатки.

Поскольку я мало пишу на Си и ещё меньше на Си++, у меня нет сложившегося мнения на эту тему. А у ЛОРа наверняка есть мнение, даже несколько.

★★★★★

Последнее исправление: xaizek (всего исправлений: 4)

Ответ на: комментарий от alysnix

Ну возможно. Я в своё время тоже не был рад сообщениям от плюсов, но это было давно, поэтому мой опыт не считается.

den73 ★★★★★
() автор топика
Ответ на: комментарий от glebiao

Ну по-моему довольно очевидно, что случаев, когда они прямо необходимы, довольно мало. Например, когда оборудование отдаёт такие данные через какое-нибудь окно в памяти. Или когда формат внешнего файла такой, прочитали байтики - получили некую запись в памяти. А если это именно программирование, то не вижу причин не использовать более безопасные аналоги.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 3)
Ответ на: комментарий от Siborgium

STL густо усыпан костылями вида «промежуточная структура-указатель» как раз для развязки памяти значения и вспомогательных структур, переживающих объекты.

Примеры

std::shared_ptr, std::list, std::deque, std::set, std::map, и близкие к ним модификации. Только std::vector и std::array не имеют вспомогательных структур, а хранят значения последовательно, все остальные упомянутые держат прокладку, которая уже ссылается на значение.

Напомню, что zero overhead – это не «бесплатно», что бы ни имелось в виду в предыдущем предложении

Да, это «то, что я назову zero overhead».

Сообщение об ошибке содержит исчерпывающую информацию об этой ошибке. В MSVS есть «сокращенный» вывод ошибок с красивыми рюшечками, а есть полный в текстовом виде. Каким все пользуются? Правильно

За годы костылестрояния разрабы наловчились фильтровать мусор из ошибок по STL, но это все равно грязный костыль, который не работает для других либ.

Конкретно какие проблемы не позволяют RAII дать какие гарантии безопасности чего?

Я уже писал с самого начала — сложные взаимные ссылки между объектами, которые неизбежно возникают в любой сложной системе. RAII дает гарантии только по поводу локальных объектов на стэке, дальше его полномочия всё.

Сборщик мусора решает проблему за счет того, что обнаруживает крупные циклические блоки и грохает их целиком, без каких-либо деструкторов. Это и есть грамотное решение, про которое я уже писал несколько раз: сначала мы деактивируем объекты, потом мы высвобождаем память. Если сразу высвобождать память, то без weak_ptr в достаточно сложной системе ты рано или поздно получишь доступ по висящему указателю. У крестовиков в таком случае виноват, кто угодно: я, ты, он, сроки — но никогда не инструмент, который безупречен и лучше быть ничего не может.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

std::shared_ptr, std::list, std::deque, std::set, std::map, и близкие к ним модификации. Только std::vector и std::array не имеют вспомогательных структур, а хранят значения последовательно, все остальные упомянутые держат прокладку, которая уже ссылается на значение.

Мдя, Вы не только школьную физику не асилили - Вы оказывается и базовые структуры данных не осознали… У меня возникает стойкое ощущение что Вы не только последние месяцы получаете деньги за написание неработоспособного кода на плюсах - Вы этим занимаетесь всю свою сознательную жизнь, ЯП тут сугубо вторичен.

Ничего что список, множество и дерево при реализации через «хранят значения последовательно» будут иметь сложность вставки или поиска O(N) и вообще потеряют всякий смысл как выделенные структуры данных? Да и вектор как бэ имеет «вспомогательную структуру» (указатель и размер), таких структур не имеет тока array…

Поздравляю Вас с пробитием очередного дна!

AntonI ★★★★
()
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от kvpfs

Они у тебя не просто знают друг о друге, ты толкуешь о каком-то сложном деструкторе, который там чего-то сложного делает, это уже странно. Ты ведь мог сделать метод deinit(), который дёрнул бы у всех объектов, а потом уже звать тривиальный деструктор. В общем проблему ты из пальца высосал

Так и делаю. Мне кажется, что мы про одно и то же пишем. Другое дело, что к RAII это отношения не имеет, оно не гарантирует вызов deinit после исключения.

Ну и кто тебе сказал, что у тебя там нормальная архитектура? Почему ты не передаёшь объекты через параметры методов лишь в контекст конкретной функции? Пишешь лютое Г, а потом экстраполируешь на всех

Кто-то должен написать логику обработки глобального состояния, ты никуда от этого не убежишь. У C++ с этим хронически всё плохо, он позволяет прекрасно обрабатывать локальные данные в стэке, но после возврата из функций обязательно должен оставаться чистый лист. Проблема в том, что хардварь, GUI, БД, сетевые сервисы, и просто асинхронщина в целом опирается на глобальные данные, «грязный лист», и стэковая организация для них не работает. А это почти все сферы применения крестов.

Когда дойдёшь до потоков ввода/вывода, обрати внимание на методы good()/eof()/fail() …

Я не спорю, что можно нагородить костылей, можно даже возвращать ошибку в thread-local переменную, можно написать какой-то там адаптер на асме... Но это всё срань, ты понимаешь? Это сложные решения простой задачи.

Выше постили ссылку на лекцию — никто не умеет в исключения. Об этом и речь.

Я не буду в это втягиваться, и лекцию читать не стану. Ремарка, чтобы не возникло ощущение, что я согласился.

Там ссылались на Джоэла:
https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/
на Реймонда Чена:
http://blogs.msdn.com/oldnewthing/archive/2005/01/14/352949.aspx

Они не могут — а ты можешь? Конечно же мы понимаем, что ты лукавишь, но ты это отказываешься признавать — ведь что ж получается, ты зря столько лет учился эти проклятые исключения правильно использовать? Куда же теперь девать «бесценные годы полученного опыта»?

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Массив можно было в язык добавить? Можно, почему-то в паскале они были,

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

но в Си попало черт знает что, и это черт знает что в доработанной форме попало в кресты, став «черт знает что++».

почему «чёрт знает»? просто универсальная заготовка.

glebiao
()
Ответ на: комментарий от den73

Например, когда оборудование отдаёт такие данные через какое-нибудь окно в памяти.

А если это именно программирование, то не вижу причин не использовать более безопасные аналоги

я извиняюсь, работа с аппаратурой (и тесно связанная задача — протоколами), это уже «не программирование»? Орригинально.

glebiao
()
Ответ на: комментарий от AntonI

Ничего что список, множество и дерево при реализации через «хранят значения последовательно» будут иметь сложность вставки или поиска O(N) и вообще потеряют всякий смысл как выделенные структуры данных?

Ты опять пытаешься изобразить меня идиотом, вырывая слова из контекста и наделяя их удобным тебе смыслом. Да, если данные из дерева хранить просто хранить линейным массивом в памяти, то это будет не дерево. Такие дела. Однако же, я писал совсем про другое, про вспомогательные структуры, которые используются при реализации деревьев.

byko3y ★★★★
()
Ответ на: комментарий от glebiao

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

Знаешь, на каких высокоуровневых языках нынче пишут под микроконтроллеры с дюжиной кб оперативы? Си, Паскаль, Standard ML — во порядке убывания популярности. Близость к железу совершенно ортогональна наличию массивов. Если у тебя динамические типы — тогда да, тогда ты не впишешься в ресурсы. Но статичные массивы никак не приводят к накладным расходам.

почему «чёрт знает»? просто универсальная заготовка

Да, универсально неудобная для любой задачи, поскольку последовала за провальной реализацией массивов в Си, и поскольку в макросах нельзя использовать список/массив, то есть, массив является второсортным гражданином.

byko3y ★★★★
()
Ответ на: комментарий от rupert

Выше постили ссылку на лекцию — никто не умеет в исключения.

Jon Kalb что-ли? Это только основы, которые должен каждый знать

Так я ж не спорю — это основы, каждый должен знать, что исключения крайне коварны, потому ими не нужно пользоваться.

byko3y ★★★★
()
Ответ на: комментарий от rupert

вспомнилось о питоне, там такая весёлая штука может выйти: типы данных меняются автоматически, и при передаче данных, можно не отследить и выйдет, что ты работаешь уже с константным типом, а об ошибке ты узнаешь по факту выполнения - неприятно.

Silerus ★★★★
()
Ответ на: комментарий от glebiao

Я имею в виду «программирование чего-то абстрактного», где стороны вольны выбирать между голыми юнионами и размеченными (хотя бы). Ведь в той же sdl почему юнионы? Потому что одна из сторон пишет на Си, а не по какой-то другой причине. Т.е. да, сэкономили несколько операций, убрав тег. Зато дыры появились. Процессоры всё быстрее, несколько операций всё дешевле, а дыры в коде - всё дороже, потому что больше задач этим процессорам доверяют. Из этого очевидно, что везде, где можно хоть насколько-то, нужно пренебречь небольшим замедлением и перейти от чистых юнионов к размеченным.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от ox55ff

Нет. С опытом приходит умение делать архитектуру, которую, в случае необходимости, можно легко подкорректировать. Просто чувствуешь, как в конкретном случае сделать лучше. Вот ты несколько раз перепишешь свой код и тоже научишься. Только нужно принять c++ каким он есть, а не пытаться тянуть подходы из других языков

Мог бы сразу написать «я получил дар наследования классов в наследство от покойного отца». Особенно мне понравилось «принять каким он есть» — я как бы с радостью, но он, падла, постоянно разный, и непонятно, какой принимать. Или ты из тех, кто будет на C++17 писать так же, как на C++03?

потому что два раза в месяц нужно переписывать иерархию

Значит плохая иерархия

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

Если в первый метод передать const, то он дальше в другие методы по цепочке тоже const будет передавать и никто не сможет изменить. А если ты делаешь параллельные потоки передачи ссылок (const, не const), а потом огребаешь проблемы, то ССЗБ. Опять проблема в архитектуре, а не в языке

Дернули метод другого объекта, другой объект держит изменяемую ссылку на исходный. И всё. Расскажешь мне, что у тебя такого не бывает, ты ссылки на неконстантные объекты не хранишь в полях?

byko3y ★★★★
()
Ответ на: комментарий от glebiao

А почему именно она универсальная? С чем ты сравнивал? Думал ли о других вариантах?

den73 ★★★★★
() автор топика
Ответ на: комментарий от rupert

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

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 2)
Ответ на: комментарий от X512

Которыми многие не умеют или осознанно не хотят пользоваться.

Рискну сделать радикальное предположение, что они тогда сами виноваты.

rupert ★★★★★
()
Ответ на: комментарий от ox55ff

std::vector<const std::string>

Зачем? Без const делай. Если нужна неизменяемость, то делай константным весь map

А потом у меня есть set таких map-ов (set<map>). Делать set тоже константным? А что дальше со сплошной константностью делать?

Ты захотел счётчик. Я тебе показал как

Я хочу распределенное key-value хранилище с многоверсионностью и строгой констистентностью. Давай, распиши мне, какие классы делать. Что, тяжеловато? Потому я привожу простые примеры, а не чтобы ты лепил отмазы, которые дальше простейших структур не работают.

Mutable не масштабируется по сложности, точно так же, как не масштабируется const, оно просто дает один дополнительный уровень описания сложности, но если класс требует два, три, четыре дополнительные уровня (когда mutable должны становиться в отдельных методах, и наоборот), то const с mutable уже не работают, они превращаются в смишные комментарии. Сложность STL — это тот уровень, где const/mutable/friend/private создают проблемы, но эти проблемы можно преодолеть. Если нарастить сложность контейнеров еще хотя бы немного, то возникнут большие проблемы с дублированием кода и обходом модификаторов.

Собственно, возвращаясь к моей изначально позиции — нельзя совать в один класс много полей. Я думаю, что здесь мы придем к общему знаменателю. Когда в классе мало полей, то const не цепляет за собой non-const поля.

Правда, что делать с const std::vector<const std::string> & — я так и не понял.

byko3y ★★★★
()
Ответ на: комментарий от den73

Полностью работает в обратную сторону: препроцессор не покрывает все применения констант, поэтому твой комментарий – неадекватная реклама.

Оно в обе стороны работает. В твоих файлах на C++ нет директив #include и #ifdef?

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

byko3y ★★★★
()
Ответ на: комментарий от kvpfs

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

Я не понимаю, ты хоть раз в жизни открывал сорцы STL? Итераторы большинства контейнеров нельзя сделать без обратных ссылок на хранилища — и это кольцевые ссылки. Это что за уровень аргументации у тебя такой? Кто из нас вчера познакомился с крестами?

byko3y ★★★★
()
Ответ на: комментарий от Siborgium

Рейнджи не абстрагируют низкоуровневые детали, а просто позволяют их более удобно компоновать. Еще раз, это клей.

Но ведь этот «клей» - это неотъемлемая часть С++, которую в будущем будут использовать вместо итераторов и которая позволит писать на С++ более высокоуровневый код, близкий к питонохаскелям. Стало быть и язык постепенно становится более высокоуровневым после выхода каждого стандарта. Как раз благодаря добавлению такого вот «клея». Даже уже между С++11 и С++98 лежит довольно большая пропасть. Как говаривал Страуструп:

Surprisingly, C++11 feels like a new language: The pieces just fit together better than they used to and I find a higher-level style of programming more natural than before and as efficient as ever. If you timidly approach C++ as just a better C or as an object-oriented language, you are going to miss the point. The abstractions are simply more flexible and affordable than before.

Заметь, он тоже говорит, что С++11 стал более высокоуровневым и абстрактным, чем раньше, и вообще ощущается как новый язык. Но ты в любых улучшениях только один клей и видишь. Странно что filesystem оказался исключением. Может и классы в С++ тоже клей тогда? Не нуачо, классы технически это же структурки с vtbl, их можно на любой сишке нарисовать, значит просто удобный клей для компоновки. И вообще ООП парадигмы не существует, есть только glorified structures и процедуры, посоны нас налюбили, расходимся.

Как наличие std::optional в STL делает С++ более высокоуровневым языком? Это чисто библиотечная фича для удобства, наколеночный optional пишется за 15 минут.

Как минимум она позволяет перейти к монадической обработке ошибок вместо кодов возврата или исключений. Как во всяких растах, эфшарпах и т.д. Довольно значительные изменения для «чисто библиотечной фичи для удобства», не?

Боже, ты реально думаешь, что рейнджи принципиально нельзя передавать в виде двух отдельных итераторов? Это попросту менее эргономично, и теряется возможность передавать возвращаемое значение из функции (т.к. оно одно) параметром, но принципиально это ничего не меняет.

Можно конечно. А еще можно вообще на асме писать. Это тоже всего лишь менее эргономично, но ничего принципиально не меняет. Серьезно, ты не видишь разницы между функциональным стилем с рейнджами, приседаниями с голыми итераторами и процедурным дрочевом с циклами и мутабельными переменными?

Секция «Garbage collection». Также рекомендую окунуться в историю и почитать связанные с этим papers.

Честно говоря, не знал. За наводку спасибо, поищу пейперы. Краем уха слышал только о Boehm GC, но всегда считал сборку мусора в С++ нонсенсом.

Другой вопрос - а был ли мальчик? Где я могу скачать С++ компилятор, поддерживающий сборку мусора из С++11? Для интереса, открываю у себя /usr/include/c++/11.2.0/memory и ожидаемо вижу следующее:

/// Inform a garbage collector that an object is still in use.
inline void
declare_reachable(void*) { }

/// Unregister an object previously registered with declare_reachable.
template <typename _Tp>
  inline _Tp*
  undeclare_reachable(_Tp* __p) { return __p; }

/// Inform a garbage collector that a region of memory need not be traced.
inline void
declare_no_pointers(char*, size_t) { }

/// Unregister a range previously registered with declare_no_pointers.
inline void
undeclare_no_pointers(char*, size_t) { }

/// The type of pointer safety supported by the implementation.
inline pointer_safety
get_pointer_safety() noexcept { return pointer_safety::relaxed; }

Конечно же, в GCC этого не было реализовано. Все так и осталось исключительно во влажных фантазиях гомитета стандартизации С++. Ну то есть, мусоросборку добавили в С++11, но нигде не реализовали, а потом и вовсе выпилили в С++23. И именно поэтому С++98 косил под джаву. Аргументация у тебя конечно уровня бох.

И все же, можно плиз список, чем именно С++98 косил под джаву. То что джаву срисовывали в том числе и с С++, это понятно. А вот в обратную сторону - это прямо что-то новенькое. Кроме как от тебя, я больше такого нигде не слышал.

archie
()
Ответ на: комментарий от archie

Как минимум она позволяет перейти к монадической обработке ошибок вместо кодов возврата или исключений

do-нотацию нормальную когда завезут-то?

Crocodoom ★★★★★
()
Ответ на: комментарий от Siborgium

Нет, это выполняет система сборки, которая – в твоей же модели – затем вызывает пакетный менеджер с нужными параметрами. Это если речь идет о «крейтах» и других подобных зависимостях.

В «моей» модели система сборки и пакетный менеджер тесно интегрированы в одну тулзу. Это не раздельные исполняемые файлы. Прописываешь в манифесте зависимости типа qt = "^6.3", делаешь что-то типа cpm install, оно тебе автоматически ставит культи из центрального репозитория, подключает к основному проекту с нужными опциями компилятора/линкера и собирает основной проект. Все пакеты в репозитории естественно переведены (обернуты) на эту систему манифестов, поэтому тулза всегда точно знает, как правильно подключить тот или иной пакет. Никакие симейковские find_package и прочие костыли вроде pkgconfig здесь не нужны. Соответственно не нужны тонны говнокода из файнд-скриптов.

Но да, тулчейн и glibc/libc через пакетный менеджер просто так не поставишь, они в систему «крейтов» не вписываются.

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

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

Это как раз тот проект, который нужно писать на С++, а не на других языках.

С этим полностью согласен. А вот то, что хромиум типовой проект - категорически нет. Проектов такого масштаба - единицы. Это ни разу не типовой проект, которому подошла бы упрощенная декларативная система сборки.

Ты молодец, вытащил удобный пример

Равно как и ты вытащил удобный для себя пример с хромиумом, для сборки которого гугл даже свои собственные GYP и GN написал.

meson ты предпочел проигнорировать, удобно.

С мезоном я знаком на уровне «полистать примеры после очередной гномоновости с лора». Верю на слово, что там с интеграцией с конаном все в шоколаде. Зато я неплохо знаком с симейком и решил потыкать для интереса тамошнюю интеграцию с конаном. Конечно же, скрипт интеграции conan.cmake не идет ни в составе конана, ни симейка. Вместо этого его предлагается выкачивать с гитхаба самому. Сам конан кстати не нашелся в официальных репозиториях арча и убунты, что уже много говорит о популярности данного поделия. Ну ок, поставил через pip, собрал хелловорлд с парой зависимостей. Поделие нагенерило различных файлов conanfile.txt, conaninfo.txt, а также файнд-шкриптов типа FindGTest.cmake. Все в лучших традициях автотулзов короче. Все кое-как прикручено друг к другу императивными соплями и генереными портянками баш симейк шкриптов.

Не ребят, эта помойка из говна и палок ни в какое сравнение не идет с тулзами аля Cargo, представляющими собой пакетный менеджер и систему сборки в одном флаконе. Пользоваться этим я конечно же не буду. Единственная нормальная вещь в этой системе - это pip, через который собственно поделие конан и ставится.

Я еще раз спрашиваю, ты лично настройки мышкой натыкивал? XML редактировал?

И мышью натыкивал, и XML редактировал, и кое-где бат скрипты дописывал для prebuild/postbuild экшенов. Не фонтан конечно, я тоже больше с MSVS связываться не желаю. Но что характерно: схема «90% на XML лапше + 10% на императивной поверщели» отлично работала для типовых рабочих проектов. Я бы и на онтопике не отказался от нечто подобного, но только в стиле Cargo, JSON/TOML вместо XML и C++ скриптов вместо поверщели. Для С++ скриптов нынче есть Cling. Но увы, крестокоммунити за десятилетия смогло только натужно родить зоопарк разношерстных утилит разной степени ущербности.

archie
()
Ответ на: комментарий от byko3y

я получил дар наследования классов в наследство от покойного отца

Я же написал, что приходит с опытом. Ничего такого сверхъестественного или уникального. В любой профессии так.

Или ты из тех, кто будет на C++17 писать так же, как на C++03?

Моя карьера началась с c++11.

Хороших иерархий не бывает

Не бывает. Но бывают такие, которые могут жить долго и удовлетворять требованиям. Понятно, что переписанная может быть лучше, но ведь старая всё ещё удовлетворяет.

Дернули метод другого объекта

void print(auto begin, auto end) const { 
for_each(begin, end, [] (auto& item) {/*...*/});
}

auto begin = vec.cbegin();
auto end = vec.cend();

removeOddElements(); // removeOddElements меняет vec. Итераторы стали невалидными

print(begin, end);

Ты про такие случаи говоришь? А зачем так писать?

ox55ff ★★★★★
()
Ответ на: комментарий от byko3y

Делать set тоже константным

Нет. Константной делай ссылку на этот set, когда передаёшь её в методы, которые только читают.

key-value

unordered_map

многоверсионностью

Непонятно. Ну допустим unordered_map c std::vector в качестве value типа.

констистентностью

Совсем непонятно. Что ты под этим подразумеваешь? У стандартных контейнеров есть предел, после которого на них не получится натянуть сову. Может лучше свой тип делать вместо матрёшки из стандартных контейнеров?

Mutable не масштабируется по сложности

И не надо. Применяется в редких сценариях.

Правда, что делать с const std::vector & — я так и не понял.

Нужно прекратить пихать const внутрь угловых скобок: std::vector<std::string> -- вот твой тип. Прекрати, просто прекрати. Когда захочешь его куда-то передать и чтобы его там не изменили, то передавай по константной ссылке: print(const std::vector<std::string>& vec);

ox55ff ★★★★★
()
Ответ на: комментарий от Crocodoom

do-нотацию нормальную когда завезут-то?

Хосспаде, крестовики за 6 лет смогли только три новых метода and_then, or_else и transform в std::optional добавить, а ты вот так сразу про ду-нотацию.

Впрочем в пропозале по std::optional крестовики начали что-то подозревать, так что у нас есть все шансы увидеть генерализованные версии std::optional и std::expected вместе с ду-нотацией. Году эдак в 2040.

archie
()
Ответ на: комментарий от byko3y

При всем моем неуважении к расту, в расте все-таки реализована настоящая константность.

Даже не знаю разочаровывать ли тебя или продолжать смотреть как рассуждаешь о вещах, в которых не разбираешься…

DarkEld3r ★★★★★
()
Ответ на: комментарий от byko3y

если данные из дерева хранить просто хранить линейным массивом в памяти, то это будет не дерево

Это зависит от того как интерпретировать массив. Скажем если интерпретировать смещение в массиве как индекс на Z-кривой Мортона то мы получаем квадро/октодерево. Упс? Так что дело совсем-совсем не в этом…

Ты опять пытаешься изобразить меня идиотом

Лучше Вас с этим никто не справится, согласен. С Вашим то феерическим талантом к заведомо неверным, но очень пафосным утверждениям по темам в которых Вы ничего не понимаете…

AntonI ★★★★
()
Ответ на: комментарий от glebiao

Если делаете исторические реминисценции, историю надо знать. Начиная с истории юникса, который, всё-таки, был не «недо – ос», а простой, но первой полноценной и практичной (то бишь, НЕ Монитором) переносимой операционной средой для мини и микро компьютеров. Других, тупо не было!

Был предок юникса, Multics, была еще ось от DEC, был VMS, совместимый с осью DEC. Собственно, на базе VMS создана винда NT, и она прекрасно переносилась меж компами. А вот Unix на то время вполне себе был приколочен гвоздями к одной архитектуре — портировался почти полностью ручками.

Поэтому и в народ пошёл. И позднее вытеснил (более, чем популярные в то время) варианты Паскаля и бодро вытеснял Фортран

Фортран и Паскаль на то время сидели в совершенно разных нишах, это как говорить, что Rust вытесняет JavaScript. В одной нише с фортраном и паскалем были C++ и Basic.

А вот Кобол почему-то нифига не бодро вытеснялся, заметь. Потому что, как я написал выше, на самом деле техническая сторона в западном IT почти ничего не решает.

Опять несколько мимо. Си – переносимый. Не знаю, задумывался ли он изначально, как переносимый, но жизнь заставила

Си изначально был препроцессором для портирования асмового кода меж машинами. То есть, он изначально не был ничем другим, он даже толком не был высокоуровневым, там даже функций в классическом алголовом понимании не было, функции были только call/ret — это был тот же асм, только портируемый.

Так вот, в 83 или около того, он сделал ассемблер для Д3-28 с синтаксисом, максимально приближенным к обычной алгоритмической записи. Никаким тебе mov, jb — обычные =, if и т.п. Но это был именно ассемблер. Умный, да. Удобный. Но Ассемблер, а не ЯВУ. Вот PL/M — язык точно такого-же класса и не более

В конце 70-х такими были (загибаем на пальцах): Си, Кобол, Бейсик, и даже в какой-то степени мультиксовый асм. То есть, не иметь аргументов у функций и даже вообще не иметь функций, а прыгать через goto по коду туда-обратно, передавая аргументы глобальными переменными, было нормой. Структурное программирование, те самые if, for, while без прыгания по меткам, и функции с настоящими аргументами, локальными переменными, возвращаемыми значениями, рекурсиями — всё это более поздние ЯП позаимствовали у алгола 60, разработанного Дейкстрой, соответственно, в 1960.

Да, если ты пишешь на Си работу с железками, то тебе нужны объединения, поскольку в Си больше ничего нет.

Да ну? Битовые поля, структуры, возможность вкладывать структуры в объединения и наоборот — столько раз, сколько нужно

Как ни странно, эти фичи (массивы, структуры, вложенности) в асмах того времени реализовывались при помощи макросов. Почти такими же сырыми и опасными они остались в Си до сих пор. Да, в самом нет PL/M структур/массивов, но их можно было реализовать на препроцессоре. Я просто пытаюсь пояснить, что Си был ничем не выдающимся языком, а кривость и опасность его примитивов не вызвана никакой низкоуровневостью — это просто наспех слепленная груда фич.

Вот никто же не заставлял K&R делать функции без прототипов, да? Никакой низкоуровневостью это не вызвано, потому что мы нынче живем с прототипами и даже не думаем рыпаться в сторону функций без аргументов, и при этом умудряемся обрабатывать переменное число аргументов. Как так? В паскале! было переменное число аргументов у read() и write().

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

Просто скажи мне, что заставило не иметь в C++ поддержки норм строк? Ладно, расширяемость и всё такое оставляем, но строки добавь! Нет, не буду. И массивы не будут, и вообще ничего не буду, оставлю сишные типы данных, только добавлю к ним конструкторы-деструкторы и функции с неявным первым аргументом. Собственно, это и был первый C with Classes, он по сути почти ничем не отличался от Си. Страуструп предпочел не вносить никаких серьезных изменений, и именно этим он так понравился манагерам из AT&T.

Вот в D пришла авторам блажь перенести битовые поля на уровень библиотеки

Не вижу какой-то серьезной значимости битовых полей.

byko3y ★★★★
()
Ответ на: комментарий от alysnix

tagged union, под названием variant record бы еще в modula-2, аж в 1979. итого фиче - 43 годика. но на самом деле, сишный чистый юнион, лучше чем tagged. из обычного union tagged делается просто. но не наоборот

Если в ЯП есть кастование указателей, то ты уже автоматически получаешь union, только неформальный. Tagged Union/Algebraic Data Type нужны именно для того, чтобы компилятор мог гарантировать корректность доступа.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Если в ЯП есть кастование указателей, то ты уже автоматически получаешь union, только неформальный.

для union компилятор вычисляет размер этого типа и совместимость по присваиванию там только к указанным в юнион типам.

а просто «кастование» не дает размера типа и кастуется как хошь и потому и опасней и неудобней(размер типа просто так не вычислить) чем простой сишный юнион

alysnix ★★★
()
Ответ на: комментарий от den73

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

Но тем не менее можно задаться вопросом, а насколько дольше профи в С++ лечит эту простыню, чем спец в другом языке X с таким же опытом лечит более простое сообщение об ошибке, выданное его компилятором

Аналогичный вопрос вдогонку (слоупочную): насколько дольше профи в C++ компилирует и тестирует ту же прогу, что спец в другом языке Y компилирует и тестирует за пару секунд?

Я сам научился на Си писать код, который работает и не сыпется с сегфолтами, но я не испытываю иллюзий по поводу того, что делать это на Си намного дольше и гарантий намного меньше. Все эти недостатки выливаются тупо в многократное растягивание сроков разработки. Если раньше это терпели, то сейчас терпят всё меньше и меньше.

это как раз будет о том, что переход от Си к Си++ - это не только приобретения, но и потери, и расширяет диапазон возможного результата данной темы

Никто не заставляет плохо использовать C++, никто не заставляет писать код на библиотеках с тяжелыми шаблонами, после чего один модуль компилируется несколько минут на современной машине. То есть, в Си просто меньше способов угробить проект, а в C++ их больше — вот и вся разница. Причем, это предмет гордости отдельных крестовиков «смотри, сколько я знаю разных способов превратить проект в нечитаемую неподдерживаемую лапшу».

byko3y ★★★★
()
Ответ на: комментарий от Siborgium

Эта ссылка была приведена в качестве иллюстрации того, как С++ попытался косить под более высокоуровневые языки на заре своего становления

Какой еще «заре»? C++11 — это скорее «начало заката».

byko3y ★★★★
()
Ответ на: комментарий от byko3y

насколько дольше профи в C++ компилирует и тестирует ту же прогу, что спец в другом языке Y компилирует и тестирует за пару секунд?

C++ поддерживает Hot Reload. C++ по интерактивности почти как JavaScript. О какой медленной компиляции ты всё время говоришь? Приходи когда твой Rust научится в Hot Reload.

https://youtu.be/x_gr6DNrJuM?t=75

fsb4000 ★★★★★
()
Ответ на: комментарий от rupert

А в питоне они, значит, менее коварные? Мягкие и шелковистые?

юнионы в C++ (комментарий)

«Да, он изначально был крайне бездарно спроектирован, эдакий «интерпретируемый Си с динамическими массивами» (то есть, не был спроектирован)»

Подозреваю, что в питоне исключения и классы с C++ слизаны. Моя первая статья на хабре была про вред исключений в питоне.

byko3y ★★★★
()
Ответ на: комментарий от den73

Из этого очевидно, что везде, где можно хоть насколько-то, нужно пренебречь небольшим замедлением и перейти от чистых юнионов к размеченным

Перефразируя слова Даннинга про капиталиста (при 300 процентах нет такого преступления, на которое он не рискнул бы, хотя бы под страхом виселицы): нет такого UB, на которое сишник не пойдет ради увеличения производительности на 3%. И тот же strict aliasing дает примерно 2% прироста. Стоило ли ради этого вносить в компилятор баг, который нельзя обнаружить ни во время компиляции, ни при выполнении отладочной сборки? Это безумие западного рынка, потому что показать 2% прироста производительности манагеру можно, а показать ему неизвестное число появившихся в итоге багов нельзя.

byko3y ★★★★
()
Последнее исправление: byko3y (всего исправлений: 1)
Ответ на: комментарий от archie

Заметь, он тоже говорит, что С++11 стал более высокоуровневым и абстрактным, чем раньше, и вообще ощущается как новый язык

Страуструп забывает сказать, какого хера он сам за 25 лет до того же не додумался. В любой момент времени он продолжает продавать себя.

На всякий случай: я здесь «намекаю» только на то, что давайте называть вещи своими именами.

byko3y ★★★★
()
Ответ на: комментарий от ox55ff

Ты про такие случаи говоришь? А зачем так писать?

А как ты проверишь, что у тебя прямо сейчас где-то в коде именно это не написано? Передал значение аргументом, аргумент оказался ссылкой, ссылка задержалась дольше, чем ты думал — и привет. Это в hello world проблема очевидна, потому что всё поведение находится на одной странице в одном месте, а C++ у нас славится щедрым изобилием неявного поведения, из-за чего ты сам можешь не знать, что у тебя на самом деле делает строчка кода.

Чтобы такое неявное поведение действительно работало, нужны железобетонные гарантии, как в Rust, где константы на самом деле являются константами. Без этих гарантий const становится просто комментарием в коде.

byko3y ★★★★
()
Ответ на: комментарий от ox55ff

Константной делай ссылку на этот set, когда передаёшь её в методы, которые только читают

Идея ясна — использовать const в минимальной области. Это некий промежуточный вариант к полному отказу от const, на самом деле, потому что как я объясню методу, что вложенные структуры тоже изменять нельзя? Я же передам ему «const map», а эта падла поменяет мне значения-вектора! Это примерно как иметь

struct Container
{
   std::string a;
   std::string b;
}

передавать ее const struct Container & аргументом, но по итогу какой толк с этой константности, если функция-получатель аргумента может поменять содержимое строк?

Опять же, еще раз: на самом деле const выполняет роль КОММЕНТАРИЯ. А если так, то зачем пытаться придумать ему какое-то более высокое предназначение?

многоверсионностью

Непонятно. Ну допустим unordered_map c std::vector в качестве value типа.

констистентностью

Совсем непонятно. Что ты под этим подразумеваешь? У стандартных контейнеров есть предел, после которого на них не получится натянуть сову. Может лучше свой тип делать вместо матрёшки из стандартных контейнеров?

Хм-м-м, похоже, я переборщил. Суть в том, что под эту задачу во всей индустрии имеется примерно 4 универсальных (не приколоченных гвоздями к другой софтине) готовых решения — она очень сложная. Настолько сложная, что ты даже этого не понял.

У стандартных контейнеров есть предел, после которого на них не получится натянуть сову. Может лучше свой тип делать вместо матрёшки из стандартных контейнеров?

Может. Но тогда есть шанс, что свой тип будет слишком сложный и тригернутся проблемы private/friend и const/mutable.

Нужно прекратить пихать const внутрь угловых скобок: std::vector<std::string> — вот твой тип. Прекрати, просто прекрати. Когда захочешь его куда-то передать и чтобы его там не изменили, то передавай по константной ссылке: print(const std::vector<std::string>& vec)

Я тогда потеряю возможность эффективно работать со значениями, которые изначально были константами. Точнее, мне придется их копировать. Абсурд C++ как раз заключается в том, что я просто хочу написать «передать аргумент, который нельзя изменять», но по факту компилятор не способен эту гарантию проверить. Мы очень долго с DarkEld3r по этому поводу спорили, мы пришли к тому, что я написал выше: const — это КОММЕНТАРИЙ, который слегонца проверяется компилятором, но неточно и ненадежно. Я-то могу передать объект по простой константной ссылке, но проверить, что объект на самом деле не меняется, можно только доскональным вычитыванием сорцов принимающей функции — именно потому я зову это комментом.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Что-то я там ничего про исключения не нашел. Но я думаю, я понял твою мысль: «всё говно».

rupert ★★★★★
()
Ответ на: комментарий от byko3y

передавать ее const struct Container & аргументом, но по итогу какой толк с этой константности, если функция-получатель аргумента может поменять содержимое строк?

У тебя какой-то особенный c++?

struct Container
{
   std::string a;
   std::string b;
};

void tryChange(const Container& item) {
    item.a = std::string("123"); // Ошибка компиляции
}

error: no match for 'operator=' (operand types are 'const std::string' {aka 'const std::__cxx11::basic_string<char>'} and 'std::string' {aka 'std::__cxx11::basic_string<char>'}) https://godbolt.org/z/1M76Wahjq

item это константная ссылка, значит всё что внутри тоже константа. Как ты изменишь содержимое? Открой книжку по c++.

Настолько сложная, что ты даже этого не понял

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

Я тогда потеряю возможность эффективно работать со значениями

Ещё раз прошу тебя. Перестань пихать const куда не нужно. Перестань, перестань, просто перестань. Когда декларируешь контейнер или структуру, то const не нужно вставлять. Вставлять нужно когда передаёшь по ссылке.

Const во время декларации это редкий случай, когда значение изначально константа. Твои контейнеры это другое. Ты же во время работы будешь их менять. Какой нахрен const в декларации. Перестань, просто перестань пихать const туда.

ox55ff ★★★★★
()
Ответ на: комментарий от alysnix

для union компилятор вычисляет размер этого типа и совместимость по присваиванию там только к указанным в юнион типам

И дальше что? Какие дополнительные гарантии это может дать? Ведь даже присваивание из «совместимосго типа» может уронить программу.

а просто «кастование» не дает размера типа и кастуется как хошь и потому и опасней и неудобней(размер типа просто так не вычислить)

std::max. Не говоря уже о том, что структуры данных динамичного размера просто невозможно описать сишными типами данных. Из-за этого очень многие протоколы/форматы имеют тягу к фиксированным размерам и телу переменного размера в хвосте — в этом плане protobuf является таким выдающимся, ведь в нём простые числа имеют переменный размер в байтах. И удачи тебе их обрабатывать через union-ы.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

И дальше что? Какие дополнительные гарантии это может дать? Ведь даже присваивание из «совместимого типа» может уронить программу.

присваивание не может. может уронить чтение и использование, если тип был ошибочно принят за нужный.

размер типа нужен для размещения переменной, построения массива таких переменных, структур с полями такого типа и так далее.

когда компилятор видит union, он вычисляет размер его типа таким образом, чтобы любой перечисленный тип физически разместился в этом union с учетом выравниваний и все такое.

std::max.

там надо брать размер по максимальному типу, да еще с учетом выравниваний. флаг в руки делать это ручками и системно независимо… когда есть union, именно для этого и предназначенный

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от fsb4000

C++ поддерживает Hot Reload. C++ по интерактивности почти как JavaScript. О какой медленной компиляции ты всё время говоришь? Приходи когда твой Rust научится в Hot Reload

Хочется ответить чем-то вроде «как называется эта команда в GDB?».

Но давай трезво оценим: чтобы такой Hot Reload работал, в коде должны быть «слоты», символы функций, куда будет подставляться новый код; производительность неотпимизированного C++ кода на том же STL чудовищно плохая, потому что всё то zero-overhead-чудо без оптимизации становится совсем не zero, и то, что с оптимизацией делалось одной асмовой инструкцией, теперь превратилось в вызов двух пар конструкторов, деструкторов, и методов объектов, то есть, стало раз в пять медленнее выполняться. Для игр такая производительность неприемлима, что есть иронично, поскольку в примере показана именно игра. Точно такая же проблема была много лет назад в лиспе: компилятор не мог заоптимизировать код, потому что вдруг сейчас кто-то захочет сделать горячее обновление?

По поводу

C++ по интерактивности почти как JavaScript
https://youtu.be/x_gr6DNrJuM?t=75

Я пару лет писал на Vue.js, там как раз было горячее обновление, но хроническая проблема этого горячего обновления заключалась в том, что ты не знаешь, корректно ли у тебя обновилась программа и ты сейчас наблюдаешь багу в коде, или же это криво прошло горячее обновление и проблема исчезнет после перезагрузки. По итогу я часто после внесения изменений перезагружал приложение — вот такая вот «горячая перезагрузка». Да, для минимальных правки по стилям, как упомянутое в видео «изменить цвет/форму», вполне работало. К сожалению, ни для чего большего оно не работает.

byko3y ★★★★
()
Последнее исправление: byko3y (всего исправлений: 1)
Ответ на: комментарий от Crocodoom

https://www.reddit.com/r/cpp/comments/6ly4rz/it_had_to_be_done_abusing_co_awa...
Нужна реализация без оверхеда. И сахарку от комитета

Да, неочевидный, но факт: async/await равнозначен продолжению через вложенный вызов функции, а это и есть форма монадичной функции.

byko3y ★★★★
()
Ответ на: комментарий от ox55ff

item это константная ссылка, значит всё что внутри тоже константа. Как ты изменишь содержимое? Открой книжку по c++

Зануда, ты заставляешь меня усложнять пример:

https://godbolt.org/z/Krc73v5Te

Константный контейнер меняется? Меняется, Никакого const_cast нету. Надеюсь, ты доволен.

upd: процитирую здесь ключевой фрагмент:

struct Container
{
   std::shared_ptr<std::string> a;
   std::shared_ptr<std::string> b;
};

void tryChange(const Container& item) {
    *item.b = std::string("Bitch");
}

Ты жонглируешь консистентностями и позируешь версионностью, а банальный const уже который день осилить не можешь

При чем тут const сам по себе? Соглашение константности содержащихся объектов в контейнерах STL — это именно что соглашение, совсем не универсальное и натянутое на STL с большим скрипом.

Const во время декларации это редкий случай, когда значение изначально константа

Может быть редкий, но что же с ним делать? Я хочу передавать константы, которые константы. Мне теперь убиться? Почему я не могу передавать константы в контейнеры STL? Да, я знаю, что есть «простой выход» плана «скопируй константу», но зачем мне тогда изначально был нужен модификатор const у этих констант?

И ситуация становится совсем печальной, когда в значения добавляются счетчики ссылок, мутексы, и кэши — я как раз привел пример неконстантных констант в чистом STL, которые создатели STL не смогли адекватно обработать.

byko3y ★★★★
()
Последнее исправление: byko3y (всего исправлений: 1)
Ответ на: комментарий от byko3y

все остальные упомянутые держат прокладку, которая уже ссылается на значение.

std::shared_ptr

Тебе действительно нужно объяснять, почему необходим control block для реализации strong- и weak- ссылок?

std::map, std::set

4.2, единственная «прокладка» там это _Rb_tree_node, которая необходима для хранения указателей на другие ноды вместе со значением. На значение она не ссылается, а хранит значение в качестве поля.

https://gcc.gnu.org/onlinedocs/gcc-4.6.3/libstdc++/api/a01067_source.html

За годы костылестрояния разрабы наловчились фильтровать мусор из ошибок по STL, но это все равно грязный костыль, который не работает для других либ.

Бездарный бред, никакого отношения к STL это не имеет и (если пояснение все же нужно) никем не используется.

Сборщик мусора решает проблему за счет того, что обнаруживает крупные циклические блоки и грохает их целиком, без каких-либо деструкторов.

Сборщик мусора не решает никаких проблем, кроме проблемы циклических ссылок.

сначала мы деактивируем объекты, потом мы высвобождаем память.

…прямо как деструктор. Если что, на С++ можно использовать кастомные аллокаторы и точно так же высвобождать память разом для множества объектов.

Siborgium ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.