LINUX.ORG.RU

юнионы в C++

 


2

4

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

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

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

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

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

★★★★★

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

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

во-первых поскольку не позволяет добавлять алгоритмы финализации, не привязанные к объекту,

Во-первых, на базе RAII и того же самого unique_ptr можно этого достичь хотя бы за счет использования кастомных deleter-ов. Это если совсем уж в лоб.

хотя на низком уровне довольно часто нужно такие делать

Во-вторых тут у вас следует потребовать список примеров. Хотя бы из 4-5-6 пунктов.

И да, RAII (в плане использования деструкторов для чистки ресурсов) можно использовать и с запрещенными исключениями. Но для этого нужно и голову включить, и руки из жопы вытащить. Получите то же самый defer.

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

Да, это тот самый Cabal, который к версии 3.0 научился в nix-style сборку, починил все проблемы с депенденси хеллом и теперь на световые годы впереди традиционных пакетников

Ого. Теперь stack можно выкидывать?

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

Что с деструкторами не так?

С деструкторами всё в порядке, если они вызываются структурировано. Если же они вызываются в рандомном порядке, который, к тому же, не видно из кода, то это создает проблему в предсказуемости поведения из-за роста комбинаторной сложности.

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

defer надо вручную писать при каждом использовании что не далеко ушло от ручного управления памяти и создаёт высокий риск ошибки во время написания программы. В случае std::unique_ptr удаление привязано к самому указателю и никаких defer писать не надо

Я точно так же могу сказать «std::unique_ptr нужно каждый раз писать вручную, что недалеко ушло от ручного управления памятью». Даже в Си макросами attribute cleanup можно сделать коротким.

Только через лютые костыли с препроцессором. И разумеется никакой типобезопасности

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

В Haiku например есть свои классы контейнеров интрузивных списков, интрузивных AVL деревьев, массивов на стеке и прочего

Что, прямо много где вам нужны AVL деревья?

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

Например для меня и некоторых других разработчиков Haiku код Линукса с glib/gtk нечитабелен и по возможности ориентируются на код *BSD

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

Читаемость же крестов сама находится под о-о-очень большим вопросом. Пробовал STL читать?

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

Но если это телега в негативном смысле, то она уже есть, и на мой взгляд достаточно адекватная: гуглить «The Dark Side of C++». Для Ъ: https://www.scribd.com/document/17341098/The-Dark-Side-of-C

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

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

О, спасибо, я примерно так и думал кстати. Но к сожалению, чтобы это прочувствовать, нужен опыт, а я дальше Qt в сторону C++ особо не ходил. Т.е. да, был ещё опыт работы с 4-этажными шаблонами, поставленную задачу я тогда решил, но мне это крайне не понравилось. И всё это было довольно давно, с тех пор язык сильно изменился. И если мы говорим об уже написанных на С++ программах, то, очевидно, они писались в разное время и содержат всевозможные антипаттерны в товарном количестве. И всё это можно отнести в графу «недостатки языка».

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

Читаемость же крестов сама находится под о-о-очень большим вопросом.

В большей степени зависит от читающего.

Пробовал STL читать?

STL специально в таком стиле написан. Приводить читабельность исходников STL в качестве примера – это еще одна демонстрация вашего понимания предмета.

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

Примерно в это время я знакомился с C++ и пришел к однозначному выводу, что на этом дерьмищи нельзя написать ничего работоспособного.

У вас лапки, болезный?

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

Читаемость же крестов сама находится под о-о-очень большим вопросом.

В большей степени зависит от читающего

Читаемость и значит «понять малознакомый код». Если «читающий» уже досконально понимает внутреннее устройство, то это не чтение, а «дочитывание»,

STL специально в таком стиле написан. Приводить читабельность исходников STL в качестве примера – это еще одна демонстрация вашего понимания предмета

Я ренжи читал, там еще страшнее. Нечитаемые сорцы либ — это правило для C++.

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

Читаемость и значит «понять малознакомый код». Если «читающий» уже досконально понимает внутреннее устройство, то это не чтение, а «дочитывание»,

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

Я ренжи читал, там еще страшнее.

Либы типа ranges или boost.spirit – это доли процента от того, что пишут на C++.

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

Я точно так же могу сказать «std::unique_ptr нужно каждый раз писать вручную, что недалеко ушло от ручного управления памятью».

Не можете. Для объявления с std::unique_ptr достаточно одной строчки, а с defer надо две или магия с шаблонами которая когда-нибудь обязательно сломается. И у меня подозрения что defer нельзя использовать при объявлении структур вроде:

struct Object {
  std::unique_ptr<SubObject1> subobj1;
  std::unique_ptr<SubObject2> subobj2;
  std::unique_ptr<SubObject3> subobj3;
}

Даже в Си макросами attribute cleanup можно сделать коротким.

Это не часть Си, а расширение конкретного компилятора GCC. В C++ всё можно сделать целиком в рамках стандарта. В Си этот аттрибут в стандарт добавлять никто не собирается.

Что, прямо много где вам нужны AVL деревья?

Используется в подсистеме ядра виртуальной памяти и в драйверах файловых систем.

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

С деструкторами всё в порядке, если они вызываются структурировано.

Деструкторы одного блока объявлений гарантированно вызываются в порядке обратном вызову конструкторов.

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

Если всё ломается от того, что деструкторы вызываются не в строго определённом порядке, то скорее всего написан говнокод.

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

И у меня подозрения что defer нельзя использовать при объявлении структур вроде

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

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

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

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

Деструкторы одного блока объявлений гарантированно вызываются в порядке обратном вызову конструкторов

Если у тебя кольцевые зависимости (а это довольно часто бывает в сложных структурах), то обратный порядок может оказаться неправильным. Или, например, для связанного списка нужно высвобождать элементы в том же порядке, в котором они были выделены. В сложной системе неизбежно всё приходит к тому, что ДЕСТРУКТОРЫ НЕДОПУСТИМО ВЫЗЫВАТЬ просто так, когда попало, перед этим объекты обязательно нужно развязать, финализировать, возможно даже чего-то подождать, а там внезапно еще и исключения могут возникнуть, и что с ними делать?

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

Если всё ломается от того, что деструкторы вызываются не в строго определённом порядке, то скорее всего написан говнокод

Пардон, ты пишешь весь свой код на shared_ptr + weak_ptr? Тогда я тебе разочарую — у тебя говнокод.

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

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

Как? Код в студию

Делаешь

struct Container
{
    struct NestedContainer field1;
    struct AnotherContainer field2;
};

void release_container(struct Container *container)
{
    release_nested(&container->field1);
    release_another(&container->field2);
}

#define Container_smartptr    attribute cleanup struct Container

Дальше в коде используешь как

Container_smartptr somevariable;

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

byko3y ★★★★
()

https://dwheeler.com/sloc/

Использованные языки, считая по убыванию строк кода - C (71% - было 81%), C++ (15% - было 8%), shell (включая ksh), Lisp, assembly, Perl, Fortran, Python, tcl, Java, yacc/bison, expect, lex/flex, awk, Objective-C, Ada, C shell, Pascal и sed.

2001-й год.

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

А вот ещё интересная табличка, которая показывает, что в полном Дебиане со всеми пакетами С++ менее чем вдвое уступает C по количеству строк, а когда-то разрыв был на порядок.

https://sources.debian.org/stats/#hist_sloc

Хотя это и не про ядро.

den73 ★★★★★
() автор топика

Две соседние темы про «недостатки C++» - эту и

Кто как борется с фактическим отсутствием приватных объявлений в C++?

ведут два чувака с околонулевым практическим опытом в C++, зато с крайне широким теоретическим «кругозором», где языки тесно переплетаются с масонами, госдепом США и мировыми заговорами

Если это не весна пришла, то что? 🙂

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

Нет, это ты опустил уровень дискуссии до

Было бы интересно другое - проследить трудовую биографию авторов уязвимостей и посмотреть, не серийно ли они этим занимаются. Хотя конечно нужно быть полным головотяпом, чтобы делать это в открытую, но что-то мне подсказывает, что и такое может быть, судя по эпическим фейлам западных спецслужб, типа создания соцсети для секретных агентов, которая, вероятно, стоила жизни многим агентам США в Иране и Китае.

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

Отрицать наличие того, что закладки вставляются системно и целенаправленно - это даже хуже, чем быть психом. Man Crypto AG

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

От того, что байты двигаются в ranges::copy вместо memcpy, язык высокоуровневым или низкоуровневым не становится.

Бред. Почитай отличия высокоуровневого языка от низкоуровневого хотя бы в педивикии что ли. Высокоуровневые механизмы отличаются в первую очередь абстракциями над низкоуровневыми деталями, скрывая их от программиста и как правило делая код более компактным и читаемым. Именно поэтому memcpy более высокоуровневая конструкция, чем портянка на ассемблере с регистрами и стеком. А ranges::copy в свою очередь более высокоуровневая вещь, чем memcpy, т.к. копирует элементы из одного контейнера в другой, или даже в поток ввода-вывода. А не заставляет возиться с адресами и байтами. Отсюда следует все остальное:

  • Полоумные указатели являются высокоуровневой абстракцией над голыми указателями, которая в общем и целом меняет ручное управление памятью на полуавтоматическое.
  • Лямбды это абстракция над указателем на функцию, а также многословным говном «функтором» (не путать с функторами здорового человека).
  • Система модулей это более высокоуровневый механизм, чем тупой и примитивный копипаст хедера препроцессором вместе с линковкой валяющейся где-то отдельно либы.
  • std::optional, std::variant и вовсе стырены с более высокоуровневых функциональных языков, пусть даже криво и убого.

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

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

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

std::vector<uint8_t> data, key;
auto result = ranges::views::zip_with(std::bit_xor(), data, key | ranges::views::cycle)
              | ranges::to_vector;

Кроме генериков все в цель. Ну и «поддержку GC» завезли лишь в С++11, не в С++98.

Ну а это вообще перл. Это чем же надо упороться, чтобы на серьзных щах сравнивать «поддержку GC» в С++11 (полоумные указатели) со сборкой мусора в джаве. Особенно std::unique_ptr косит под джаву, вот прямо один в один.

В общем так и не услышал вменяемых аргументов, чем именно С++98 косил под джаву.

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

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

Какие именно возможности системы? Напоминаю, что при наличии пакетного менеджера полностью отпадает необходимость шариться в системе и искать там нужные хедеры и либы. Если надо проверять ОС, архитектуру и тип компилятора - ну так это элементарно делается декларативными if-выражениями, типа сишного пропроцессора. Различных ОС и компиляторов не так много, система сборки может сама все заранее проверить и потом давать писать условия типа if os == linux && arch == x86_64.

Я уже послал смотреть сборку Chromium и других известных проектов на С++. Пропорции ровно обратные: 90% нетиповых сценариев и 10% типовых.

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

Вот например CMakeLists.txt для популярной библиотечки json. Куда как более типовой проект, чем хромиум. В симейк файле вижу в основном настройку таргетов и их свойств. Все это прекрасно можно сделать и через манифест типа Cargo.toml. Какие такие определения возможности системы здесь нужны?

Нет, может, хотя это не его прямое предназначение. См. ExternalProject_Add и FetchContent.

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

Conan интегрируется с CMake и meson и отлично работает с ними. Зачем создавать что-то еще – неясно.

Это ты про модуль cmake-conan, которого нет в стандартной поставке CMake, который надо выкачивать с гитхаба в скрипте, прикручивать императивными соплями и для каждой зависимости набивать горы копипаста из conan_cmake_configure/conan_cmake_autodetect/conan_cmake_install? Прямо верх юзабилити, угу. Удобным это может показаться разве что для зашоренных крестовиков, десятилетиями сидящих в крестоболоте, и нормальный тулинг с нормальным UX видевших только на картинках.

Будешь регулярно делать – поймешь, почему на оффтопике CMake абсолютный чемпион. При всех его проблемах он намного лучше.

Это скорее из-за общего тренда на кроссплатформенность и падения популярности студии в пользу вскода, а не из-за каких-то преимущества CMake. Помню лет 6-7 назад в одной команде виндузня категорически не желала слезать со студии и закатывала анальные истерики при любой попытке перехода на CMake. Ибо надо какие-то мутные шкрипты сидеть писать, а не настройки мышью натыкивать. Ситуация медленно начала меняться только после того, как мелкософт добавили официальную поддержку CMake в студию. И то сидельцы на оффтопике до сих пор испытывают фантомные боли при использовании симейка. Если очередной проект вантуз-онли, то они так и норовят засетапить его в студии без всяких симейков. Так что исходя из личного опыта, чет не тянет CMake на абсолютного чемпиона.

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

У меня опыт следующий.

Когда я почитал про С++, я сказал «вау!», и сразу начал тянуть в программу объекты, исключения, шаблоны, inline, constexpr и т. п. И я за забуксовал, так как большинство усилий уходило не на саму логику программы, а на размышления вроде «а могу ли я сделать это через constexpr» или «а является ли этот деструктор exception-safe» и т. п.

И потом я понял свою ошибку. Если вы переходите с С на С++, нельзя сразу пытаться использовать все фичи С++. Нужно писать как на С, и поэтапно по одной фиче начинать использовать нововведения С++. Только так. И только когда какая-то фича уже вошла в вашу практику, вы поняли все её подводный камни, паттерны и антипаттерны, только после этого нужно внедрять следующую фичу. То есть нужно ограничивать себя в количестве фич, которые используются. Я бы рекомендовал начать с объектов, исключений, STL, а последнее что я бы рекомендовал использовать - перегрузку операторов, шаблоны, rvalue, constexpr и другую compile-time магию.

Еще что у меня сработало - это пописать на другом языке, и попытаться привнести те же практики в С++. Например, я немного покодил на Python, и начал осознанно чаще использовать в С++ конструкции for (auto &i: lst) вместо Си’шного for (i = 0; i < len(lst); ++i) или С++ for (i = lst.begin(); i != lst.end(); ++i)

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

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

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

Я бы рекомендовал начать с объектов, исключений, STL, а последнее что я бы рекомендовал использовать - перегрузку операторов, шаблоны, rvalue, constexpr и другую compile-time магию

А я бы рекомендовал вообще ни к чему из перечисленного не прикасаться. Ну, разве что объекты можно оставить, только без наследования реализации, и часть стандартной либы, которая без контейнеров, filesystem, functional, iostream. Ну и за Boost расстреливать на месте. Это очень продуктивный подход, но он немедленно разбивается об «у нас тут была команда, которая поставила себе за правило задействовать каждую имеющуюся фичу крестов», и по итогу ты открываешь код и такой «это кресты?», потому что ты никогда в жизни не видел такого синтаксиса... а он есть, компилируется и выполняется.

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

Объекты нужны и обязательно с конструкторами/деструкторами (важно для управления ресурсами). Наследование - ну я бы не ограничивал, там где нужно почему бы и нет. Для начала можно без абстрактных классов, миллиона типов конструкторов (типа константных, перемещения и т. п.), множественного наследования, друзей и т. п.

С STL практически полностью пропадает необходимость ручного управления памятью. Начать с std::vector и std::string.

Исключения - must-have для нормальной обработки ошибок. Да и STL без них нормально не поиспользуешь.

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

Объекты нужны и обязательно с конструкторами/деструкторами (важно для управления ресурсами)

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

Наследование - ну я бы не ограничивал, там где нужно почему бы и нет

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

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

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

С STL практически полностью пропадает необходимость ручного управления памятью. Начать с std::vector и std::string

Да, вместо ручного управления памяти ты получаешь непонятно что с непонятно какой производительностью. Контейнеры STL были попыткой сделать из крестов высокоуровневый инструмент аля питон, чтоб ассоциативные массивы писались в две строки, но по факту получилось ни рыба, ни мясо: для низкого уровня слишком большие накладные расходы, а для высокого уровня слишком много острых углов (UB и ошибок работы с указателями) и чудовищно вырвиглазнейшие сообщения об ошибках — fsb4000 тут когда-то постил рекордной длинный выхлоп компилятора на одну ошибку использования STL, там что-то типа 150 ошибок было. Но получить ошибку из STL на пол-экрана — это полнейшая норма.

Исключения - must-have для нормальной обработки ошибок. Да и STL без них нормально не поиспользуешь

Да, в том числе поэтому STL отправляется на помойку — потому что плохо работает без исключений.

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

И константные методы тоже в большинстве случаев являются довольно бессмысленными приседаниями

Вот это ты отжёг так отжёг. Ты как тогда собрался вызывать методы на константных объектах?

Страшно представить как выглядит твой c++ код с таким мировоззрением. Но виноват цпп, ага.

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

И константные методы тоже в большинстве случаев являются довольно бессмысленными приседаниями

Вот это ты отжёг так отжёг. Ты как тогда собрался вызывать методы на константных объектах?

Смотри следующее предложение:

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

Очевидно, порицаю понятие «константный объект» в непримитивных случаях. И это действительно так. Буквально недавно уткнулся в то, что «константный объект» нужно модифицировать. Каждый раз когда ты применяешь константность для непримитивного типа, ты быстро упираешься в «как мне поменять константный объект?» — а нахрена ты его делал константным? Дальше идет «так в книжках написано», или «ну есть же в языке константные объекты, надо их как-то применять», или, как более тонко ответил DarkEld3r, «чтобы пометить для пользователя, что этот объект желательно не изменять» — вот такая вот у нас «константность». При всем моем неуважении к расту, в расте все-таки реализована настоящая константность.

Страшно представить как выглядит твой c++ код с таким мировоззрением. Но виноват цпп, ага

А мне страшно смотреть на книжную классику. На я уверен в своей правоте, потому что со мной согласен гугл.

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

Ты как тогда собрался вызывать методы на константных объектах?

Есть мнение что мы особо ничего не потеряем если не будем использовать константные объекты

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

ты быстро упираешься в «как мне поменять константный объект?» — а нахрена ты его делал константным?

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

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

Есть мнение что мы особо ничего не потеряем если не будем использовать константные объекты

без строковых констант ты даже хелловорд не напишешь

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

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

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

std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >,
std::basic_string<char, std::char_traits<char>, std::allocator<char> >,
std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> >
  >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>,
std::allocator<char> > const, std::basic_string<char, std::char_traits<char>,
std::allocator<char> > > > >

Это один идентификатор типа, если что.

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

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

Проблема в первую очередь в том, что каша в голове.

Это один идентификатор типа, если что.

Могли бы хоть отформатировать нормально.

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

без строковых констант ты даже хелловорд не напишешь

Ололо, в крестах нету своих строковых констант, токмо сишные const char * и const char []. Менее очевидный факт — в сишке максимально ублюдочная реализация массивов, просто квинтэссенция неудачных решений: и синтаксис объявления «посмотри направо, потом налево, потом выйди за скобки, потом направо, потом налево, потом за скобки»; и «переменная-массив является ссылкой на нулевой элемент», из-за чего массивы копировать нельзя, хотя структуры можно, а также про приходе по смешанным структурам данных переменные весело меняются со ссылок на значения и обратно; и отсутствие учета числа элементов в динамическом массиве, благодаря чему ты можешь стрелять себе в ногу как возжелаешь — отсюда легендарные нулевые терминаторы массивов и sentinel record-ы с O(N) масштабируемостью, которые иногда затираются и приводят программу в плохо предсказуемое состояние.

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

Это один идентификатор типа, если что.

Могли бы хоть отформатировать нормально

То есть, к пятистрочному идентификатору типа претензий нет, а к форматированию - есть? OK.

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

Наследование - ну я бы не ограничивал, там где нужно почему бы и нет

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

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

Мой недавний пример, правда из мира Python, но это сути дела не меняет. Есть базовый класс - список. Мне нужен точно такой же, но с небольшой дополнительной логикой. Был вариант сделать свой класс, а данные (список) поместить в переменную класса. Но тогда приходилось заново описывать все функции по работе со списком. Поэтому было проще отнаследоваться от списка и переопределить/добавить лишь пару функций.

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

STL

с непонятно какой производительностью

Роль производительности сильно преувеличена.

В большинстве случаев производительности обычного кода вполне достаточна. Но люди всё равно при первой возможности продолжают пихать inline’ы, упарываться compile-time конструкциями, а недавно я открыл такую штуку как Branchless programming, в котором товарищ утверждает что if’ы медленные и можно без них. Боязнь STL из-за якобы «проблем с производительностью» - в ту же корзину. Мало того, что порой это приводит к обратному эффекту - замедлению, но еще и уводит фокус программиста от логики самой программы, а также почти наверняка привнесёт новые увлекательные проблемы и побочные эффекты.

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

Если у вас и правда много вычислений, то имеет смысл следовать простой логике: 1) сначала заставь работать, потом оптимизируй 2) ускоряй только те места, которые медленные, подтверждая каждое улучшение измерениями. И будет счастье.

Поэтому STL в подавляющем большинстве случаев - отличный выбор.

а для высокого уровня слишком много острых углов (UB и ошибок работы с указателями)

Если программер не умеет работать UB, то ему и в С и в С++ лучше не соваться.

По поводу указателей не понял: использование стандартных контейнеров как раз и преследует цель самостоятельно не выделять/освобождать память (где это возможно), к чему тогда указатели?

Исключения - must-have для нормальной обработки ошибок

Да, в том числе поэтому STL отправляется на помойку — потому что плохо работает без исключений.

Дело не в STL. В Си у тебя почти любая функция должна возвращать два типа значений: одно - то, которое ей (функции) полагается по логике, второе - код ошибки если возникла исключительная ситуация. В Си нужно делать специальные приседания, чтобы такое реализовать. И даже с приседаниями, код превращается в лапшу с этажами if’ов. Но это не главная проблема. Главная проблема состоит в том, что по выше озвученными причинам люди просто забивают на обработку ошибок. С++ решает эту проблему, потому как требует от программиста просто написать throw; даже если ты не словил исключение, ты хотя бы корректно закроешь то, что нужно корректно закрыть (если, конечно, ты следовал RAII).

Да, настоятельно рекомендуется потратить 2 часа на прослушивание Exception-Safe Code часть 1 и часть 2, ну, или эквивалент). Да, я видел индивидуумов, которые пытаются писать логику на исключениях. Но в целом плюсы от исключений с большим отрывом перевешивают минусы. Да и это уже стандарт де-факто не только в плюсах (а нормальный программист, по моему мнению, должен уметь писать на нескольких языках, хотябы на базовом уровне).

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

Ололо, в крестах нету своих строковых констант,

там чужие строковые константы что ли? в том и фишка, что в плюсах можно делать классы, что выглядят как встроенные типы.

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

массивы в плюсах - наследие си. а в си массив - просто последовательность элементов с размером, известным только во время компиляции. «настоящий массив» требует хранения его реального размера (отсюда спец форма операторов new/delete для массива).

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

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

Смотри следующее предложение

Там про примитивные типы.

Очевидно, порицаю понятие «константный объект» в непримитивных случаях

Нам не очевидно.

Буквально недавно уткнулся в то, что «константный объект» нужно модифицировать.

Значит этот объект не должен быть константным.

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

std::vector<int> items; // изначально объект не константный

print(items);

void print(const std::vector<int>& items) {
// здесь мы можем вызывать только константные методы

// если items.begin() не был константным, то мы бы не смогли распечатать содержимое вектора
}
ox55ff ★★★★★
()
Ответ на: комментарий от Kroz

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

Если я потокам передам этот объект в виде ссылки на константный объект, то это явно укажет, что изменение запрещено, разрешено только чтение. А при попытке изменения будет ошибка компиляции.

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

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

Передёргиваешь. Зачем ты вместо std::string взял basic_string с явным указанием шаблонных параметров? Они там отличаются от дефолтных.

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

ox55ff ★★★★★
()

йклмноп.

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

Метод обеспечения безопасности через кастрацию моден, но непродуктивен.

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

Не надо заниматься начётничеством и впадать в фанатизм. Опасность – опасность… Розетка 220 тоже опасна. И? Каждому инструменту – своё место.

PS: у ребёнка был изумительный шуруповёрт, из весёленькой жёлтенькой пластмассы. Совершенно безопасный и столь же восхитительно бесполезный.

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

Батенька, что за растаманы вам повсюду мерещатся? Где ты в посте увидел упоминания раста? С каких пор вообще den73 связан с растом?

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

Если у тебя кольцевые зависимости (а это довольно часто бывает в сложных структурах)

Брехня, скорее всего вытекает из твоего патологического неприятия наследования, в итоге выходит циклический говнокод, почти всегда такое можно развязать. Лишь однажды заюзал shared_ptr + weak_ptr в подобном случае.

Пардон, ты пишешь весь свой код на shared_ptr + weak_ptr? Тогда я тебе разочарую — у тебя говнокод.

Есть ли аргументы против кроме фантазий?

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

Неужели мастер не осилил?

struct Q {
  Q(int *error_code) {
     ...
     *error_code = 4;
  }
};

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

Это один идентификатор типа, если что.

Для пущего эффекта в следующий раз пости это:

namespace shit {
	namespace crap {
		template <typename CharT,
				 typename Traits,
				 typename Alloc,
				 typename Out_of_hate,
				 typename Die,
				 typename Fucked_shit,
				 typename Placeholder
		using fucked_shit_std_cpp_basic_string = std::basic_string<CharT, 
				Traits, Alloc>;
	}
}

std::map<shit::crap::fucked_shit_std_cpp_basic_string<char,
	std::char_traits<char>, std::allocator<char>, unsigned long long,
	unsigned long long, unsigned long long, unsigned long long>,
	...>;

Это только первый параметр шаблон, если что ).

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

Тебе везде мерещатся психи и теории заговора?

@byko3y это известный адепт теории лунного заговора, утверждавший что с точки зрения физики амеры не могли долететь до луны. Вот прямо никак. Правда физики он не знает вообще, у него в голове псевдофизическая каша, но мнение имеет и отстаивает его яростно (могу дать пруфов с его перлами).

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

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