LINUX.ORG.RU
ФорумTalks

Опрос: Три-пять лучших фич C++

 , ,


3

4

Поскольку я с недавних пор устроился на работу, то простыни временно закончились. Тем не менее, каждый раз при работе с сишным кодом снова и снова всплывает мысль «блин, как же здесь не фатает крестов». Но в остальном сишки мне более чем хватает, мне не нужны геттеры-сеттеры, классы-интерфейсы под single responsibility, и прочая мура. Пару источников вдохновения:

https://google.github.io/styleguide/cppguide.html
https://yosefk.com/c fqa/

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

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

Итоги по состоянию на 24.12.2021 22:00 (MSK): https://pastebin.com/bxamaGDY

★★★★

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

STL не хватает, если писать на чистом Си

Напоминаю, что STL делеко не бесплатна. Intrusive containers бесплатны, да. А так-то под сиху есть и хэш-таблички готовые.

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

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

У тебя есть какая-то статистика из внешнего независимого источника, кроме личного мнения?

Это круто, но тебя не смущает тот факт, что Rust за N лет так и не смог показаться каких-то выдающихся резуьтатов по скорости компиляции?

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

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

Так это ведь ты мешаешь, не я. Логические ошибки допустимы в любом языке. Поэтому их количество будет примерно одинаковым везде (при прочих равных). Но в Плюсах к этому классу ошибок также добавляется UB, с которым постоянно бороться надо. И в этом контексте ещё большой вопрос, кто выкатит к вечеру приложение быстрее: программист на Раст, который будет ждать компиляцию чуть дольше (да и то не факт), или программист на Плюсах, пытающийся совладать с очередным UB.

потому что на низколиквидном рынке сегодня 5 вакансий на 2 соискателя на овермиллион денег, а завтра 1 вакансия на 2 соискателя и платят как за PHP.

И причина - неадекватная сложность Плюсов. В других сферах этого ЯП практически нет.

Давай я научу тебя некромантии Учитывая то, что автор продавал либы под F#, можно предположить, что он был не совсем искренен в описании характеристик онных.

А, точно, про web.archive забыл как-то, спасибо, что напомнил. Допустим, что автор приврал на счёт сроков. Но вряд ли он врал по поводу конечного результата и того, что команда плюсовиков с оптимизацией кода не смогла справиться. Изначальный тезис о сложности плюсов это никак не отменяет. Возвращаясь к спецификации языка (которая, напомню, составляет около полутора тысяч страниц) вряд ли ты это сможешь оспорить.

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

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

Хз как сейчас, но раньше возлагали надежды на альтернативный бекенд (Cranelift), который можно использовать для быстрой сборки. Для цикла «внёс изменения, прогнал тесты» может и нормально будет.

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

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

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

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

У тебя есть какая-то статистика из внешнего независимого источника, кроме личного мнения?

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

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

Где ты был во время моих растосрачей? Я там раскрывал что для «разработчиков компилятора» было в приоритете. Коротко — закрыть проект и уйти в закат. У них самих были очень большие проблемы со времнем компиляции, но это как бы не баг, а фича: «Рон, чем ты занят? — Я компилятор компилирую. — А, ну ладно».

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

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

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

Потому, как ни странно, Rust провоцирует увеличение числа логических ошибок из-за отвлечения внимания. Код на расте засран вспомогательными конструкциями еще сильнее, чем крестовый, потому что в крестах отдельное жонглирование было допустимо ценой UB, а в Rust по идеоматике ты должен делать три раза «ку» на каждый чих, и только так. Из-за чего либы на расте по большей части составлены из обёрток-интерфейсов (не путать с обертками к сторонним сишным либам).

Но в Плюсах к этому классу ошибок также добавляется UB, с которым постоянно бороться надо. И в этом контексте ещё большой вопрос, кто выкатит к вечеру приложение быстрее: программист на Раст, который будет ждать компиляцию чуть дольше (да и то не факт), или программист на Плюсах, пытающийся совладать с очередным UB

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

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

потому что на низколиквидном рынке сегодня 5 вакансий на 2 соискателя на овермиллион денег, а завтра 1 вакансия на 2 соискателя и платят как за PHP.

И причина - неадекватная сложность Плюсов. В других сферах этого ЯП практически нет

А кресты тут при чем? Я же про рынок труда на сишке писал.

Изначальный тезис о сложности плюсов это никак не отменяет. Возвращаясь к спецификации языка (которая, напомню, составляет около полутора тысяч страниц) вряд ли ты это сможешь оспорить

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

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

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

Опрос показал, что 100% пользователей интернета пользуются интернетом.

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

Опрос показал, что 100% пользователей интернета пользуются интернетом.

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

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

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

Ну вот видишь, ты всё понял, а задаешь вопросы.

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

Ну вот видишь, ты всё понял, а задаешь вопросы.

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

Или это только ты, весь из себя «архитектор» видишь на два шага вперёд, а остальные тупые?..

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

К слову, это тоже интересный пассаж. Ты правда не видишь, что некоторые сознательно выбирают «строгие ограничения»? Причём раст тут ещё далеко не самый строгий. Упарывания (зависимыми) типами, формальные доказательства - это ведь в ту же степь. Да, совсем не мейнстрим, но энтузиасты этого дела есть.

Впрочем, вокруг раста действительно есть люди, которым он нравится за определённые свойства, а другие они не (по/при)нимают. Отсюда периодически возникают пожелания о «расте с GC».

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

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

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

Ты правда не видишь, что некоторые сознательно выбирают «строгие ограничения»?

Я тебе даже об этом сам писал. Некоторые даже предлагаю устроить тюрьму с колючей проволкой и смотрителями с автоматами на вышках. Отсутствие UB — это приятно, но это далеко не решает все проблемы с отладкой и тестированием кода. Вспомни Ariane 5, который тоже был написан на языке без UB, но по итогу прога упала по переполнению числа — и кому какая разница, UB это или не UB? Приложуха не работает, с UB или без UB, отсутствие UB просто делаете разработку проще и предсказуемее — естественно, при прочих равных. А в случае Rust это нифига не «прочие равные» — код становится сильно сложнее и это само по себе является почвой для возникновения ошибок. И по итогу этот абсурд пришел к тому, что заткнули проблему с одной стороны, а баги теперь вылезают с другой стороны.

Отсюда периодически возникают пожелания о «расте с GC»

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

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

Это очевидно, что среди жрущих кактус сильно больше тех, кого устраивает поедание кактуса.

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

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

Я-то думал, что для этого достаточно капитана очевидность. А архитектор уже может предложить как с этим проще жить.

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

Продолжаем играть в аналогии: можно воспринимать это как тюрьму, а можно как «порядок». Кому-то жизнь не мила без возможности насрать в подъезде - как же так, загоняют в рамки и лишают «свободы».

Отсутствие UB — это приятно, но это далеко не решает все проблемы с отладкой и тестированием кода.

Не решает, да. Только ты опять споришь с собой - я ведь такого и не говорил.

А в случае Rust это нифига не «прочие равные» — код становится сильно сложнее и это само по себе является почвой для возникновения ошибок.

Раст-то не просто в вакууме существует. С чем сравниваем? С какой-нибудь джавой? Ну да, так и есть. С С++? В этом случае я с тобой не согласен. Вот чего не понимаю, так это почему человек, которому относительно комфортно писать на С и который может ограничиваться подмножеством С++, на расте моментально находит сложность. Я заранее готов признать, что не всё в языке сделано идеально, даже с учётом осознанно выбранных ограничений: хватает и кривых и недоделанных мест. Но писать-то тоже можно по-разному.

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

Да. И что? Иногда у меня складывается ощущение, что ты реагируешь на произвольно выбранные ключевые слова и в ответ выдаёшь какой-то пространный текст. Если тебя устраивает язык с GC - бери его, чего тут думать.

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

Раст-то не просто в вакууме существует. С чем сравниваем? С какой-нибудь джавой? Ну да, так и есть. С С++? В этом случае я с тобой не согласен. Вот чего не понимаю, так это почему человек, которому относительно комфортно писать на С и который может ограничиваться подмножеством С++, на расте моментально находит сложность. Я заранее готов признать, что не всё в языке сделано идеально, даже с учётом осознанно выбранных ограничений: хватает и кривых и недоделанных мест. Но писать-то тоже можно по-разному

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

Я хочу четко выразить свою позицию: Rust не хуже. И не лучше. Он позаимоствовал большую часть проблем с C++ почти без изменений, поскольку его писали корпоративные утята, для которых C++ был эталоном. И крестовики же теперь ратуют за распространение C++, но делают они это только потому что и уже задостал C++, у них банальная позиция «всё, что угодно, только не тот C++, на котором я пишу последние пять лет». И раст будет им нравится ровно пока они не занырнут в него головой, и Rust станет из «top loved» «top hated», и пойдут комиксы на xkcd про то, что раньше я полдня компилировал кресты, а теперь вторую половину дня воюю с borrow checker-ом, который в расте фантастически тупорылый и любые нетривиальные случаи ему нужно кормить с ложечки ручными описаниями областей видимости.

Иногда у меня складывается ощущение, что ты реагируешь на произвольно выбранные ключевые слова и в ответ выдаёшь какой-то пространный текст. Если тебя устраивает язык с GC - бери его, чего тут думать.

«Язык с GC» — это абсолютно любой язык. Вопрос лишь в том, как в нем реализованы эти интерфейсы, прибиты ли они гвоздями к синтаксису и инетрпретатору-компилятору, или же строятся уже на уровне семантики.

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

поскольку его писали корпоративные утята, для которых C++ был эталоном

Это достаточно спорно. Начиная с того, что первые версии раста были написаны на OCaml. Плюс бытует мнение, что раст лучше подходит как замена С, чем С++. С последним я (тоже?) не согласен, но причины у нас будут явно разные.

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

Таких людей, конечно, немало. Тем не менее, у меня ситуация была другой: изначально наоборот часто возникали ситуации «блин, а в плюсах можно вот так сделать, а в расте нельзя/сложно». С какого-то момента понял, что к С++ (даже аккуратно выбранному подмножеству) возвращаться не хочу. Конечно, были и разочарования с растом, но в целом жить можно. Если сделают что-то лучше - замечательно, но Nim и Zig не впечатлили.

«Язык с GC» — это абсолютно любой язык. Вопрос лишь в том, как в нем реализованы эти интерфейсы, прибиты ли они гвоздями к синтаксису и инетрпретатору-компилятору, или же строятся уже на уровне семантики.

Можешь раскрыть мысль про «GC на уровне семантики»?

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

Начиная с того, что первые версии раста были написаны на OCaml

Да, первые версии раста написал романтик.

Плюс бытует мнение, что раст лучше подходит как замена С, чем С++. С последним я (тоже?) не согласен, но причины у нас будут явно разные

Исключения же. У крестов эдак 90% библиотек становятся неюзабельными без исключений.

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

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

Можешь раскрыть мысль про «GC на уровне семантики»?

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

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

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

Честно говоря, подзабыл. Я правда такое писал? Массово перебираться с С++ на хаскель точно не призываю. Даже если вдруг все плюсовики поголовно полюбили хаскель, то в этом всё равно мало смысла. Лично мне хаскель интересен - это да. Но признаю честно: я до сих пор на уровне «почитываю что-то периодически», нормальной практики нет. Вероятно, это плюс-минус сравнимо с твоим знакомством с растом.

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

Питон. Питон написан на Си.

Ну это «не считается».

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

Это ведь не будет «честный GC для всего»? Про питон не в курсе, но была (есть?) такая штука как «C++/CLI» (ранее «Managed C++») - тоже можно сказать, что это С++ с GC, но на самом деле это ни то ни сё.

В целом понятно, что можно прикрутить GC к любому языку и наоборот: в языке с GC использовать «ручное» управление памятью. Иногда это даже имеет смысл, но «традиционное» для языка управление памятью будет эргономичнее.

DarkEld3r ★★★★★
()

Я сейчас книжку по крестам ковыряю, и тут прямо трешак-трешаков: «для того, чтобы ограничить доступ к члену класса извне, объявляем этот член private.. А поскольку нам все-таки нужно иметь доступ к члену класса извне, то назначаем friend функции», или «поля const объекта нельзя менять но если очень хочется, то можно, потому для изменения полей const объекта в const методах мы назначаем полям атрибут mutable». Ну то есть что-то вроде «чтобы система не падала — подставим костыль, а чтобы не упал костыль — поставим костыль под костылем».

У меня вопрос: почему весь интернет еще не завален смехуечками на эту тему?

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

почему весь интернет еще не завален смехуечками на эту тему?

Грешно смеяться над больными людьми!(c)

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

для изменения полей const объекта в const методах мы назначаем полям атрибут mutable»

Дык в джаве или C# просто нет константых методов (и заодно константых объектов) и всё. Только поэтому «костыль» и не нужен. При помощи mutable можно делать кеширование или прочие вещи обеспечивания «логическую константность». Плюсы можно пинать за разные кривости, но именно тут ты зря придрался.

А поскольку нам все-таки нужно иметь доступ к члену класса извне, то назначаем friend функции

Так мы не просто «разрешаем (всем) доступ извне» - доступ выдаётся конкретной функции (или классу), причём это является частью интерфейса класса: посмотрев на определение класса ты можешь увидеть всех «друзей», это не станет сюрпризом. Как правило без этого действительно можно обойтись, кроме редких исключений вроде операторов.

Честно говоря, то, что ты только об этом узнал, а вовсю рассуждаешь о С++ уже давно, вызывает некоторые вопросы. (:

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

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

Да, я про mutable первый раз узнал, я даже в либах этого счастья не видал. В той же MS STL mutable присутствует только в тестах, в самой либе ни одного mutable поля нету, и я подозреваю, что применение mutable — это признак говнокода. Friend же в STL применяется в трех файлах:

inc/xnode_handle.h 
	Line 197:     friend void swap(_Node_handle& _Left, _Node_handle& _Right) noexcept /* strengthened */ {
inc/xpolymorphic_allocator.h
	Line 193:         friend class polymorphic_allocator;
tools/inc/stljobs.h
	Line 81:     friend void swap(handle& lhs, handle& rhs) noexcept {
	Line 224:     friend void swap(tp_io& lhs, tp_io& rhs) noexcept {

Я сам довольно неопытный кодер на крестах, только мелкие софтины пока что писал. В основном я все-таки пишу public.

По поводу const не нужно думать, что я конченный ассемблерщик «если ячейка доступа на запись, значит я ее буду менять». Но то, как в крестах реализованы const — это бред. Например, vector<const int> является не совместимым с vector<int> типом. Причина очевидна. если вспомнить, что добавление элемента в vector<const int> приводило бы к появлению этого элемента в связанном vector<int>. Другое дело, что меня не колышат причины — меня волнует только удобство работы с языком, а работать с «этим» неудобно.

Как я люблю говорить «не можешь срать — не мучай жопу». Если у тебя переменная не const, то пиши ее без const, а логическую константность соблюдай на уровне контрактов как захочешь. Если у тебя поле private, то оно private, без исключений. Если его дергает дюжина левых функций. то это не private и не нужно пытаться его объявлять как private — вот и вся история. От этих private, разложенных куда попало, у меня уже всё лицо в синяках (правда, это было в паскале), потому что начинаешь кастомизировать либу. private тут, private здесь, расширить ничего нельзя. Типовое такое ООП, синоним нерасширяемости и неподдерживаемости.

При том, что в конечном итоге истинного private в крестах не существует, поскольку private поля являются частью интерфейса класса — это самая удивительная фича крестов.

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

подозреваю, что применение mutable — это признак говнокода

Самый банальный пример: есть у тебя метод, который что-то там сложно вычисляет (или долго откуда-то получает). По смыслу он константный. В какой-то момент решаешь сделать кеширование - от его наличия или отсутствия логика происходящего и «контракт класса» вообще никак не меняется. Конечно, можно убрать константность у метода (создав проблемы себе и другим пользователям кода), но зачем?

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

Friend же в STL применяется в трех файлах

И что это должно доказывать?.. Опять подброшу примеров.

Предположим, есть у тебя класс Х для которого ты хочешь реализовать сложение. Если складывать только Х + Х, то проблемы нет - можно это и как метод реализовывать. А если хочется int + Х, то уже придётся делать свободную функцию. Если нужен доступ к потрохам класса, то без friend никак. Можно ли без этого обойтись? Конечно: например, пожертвовать эргономикой или выставить наружу «лишние» внутренности.

Ещё один пример - тесты. К слову, в расте последнее решается тем, что public/private работает вне модуля - внутри него всё всем доступно, а значит и (внутримодульные) тесты могут ковыряться во внутренностях как хотят.

Например, vector является не совместимым с vector типом.

Помню, что ты жаловался на это в какой-то из тем, но, честно говоря, не вижу проблемы. И это так не const работает, а спецификаторы типов: int, const int, volatile int, int& - это всё разные типы. Опять же, это всё не относится напрямую к спецификаторам методов. Лично я нахожу весьма удобным, что методы можно разделить на константные и нет. И наоборот: когда иммутабельность, которую делают «изнутри» класса кажется странной.

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

«Соблюдение контрактов на словах» плохо работает. Я бы даже сказал, что не работает вообще. Это примерно как писать в комментариях - без проверки компилятором оно быстро устаревает и рассинхронизируется с кодом. Да, не всё можно (или имеет смысл) переложить на компилятор, но не вижу смысл отказываться от того, что вполне неплохо работает.

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

Чем эти «левые» функции принципиально отличаются от методов? Просто потому что ты не привык? Их набор заранее известен, их видно из объявления класса - всё как с методами.

поскольку private поля являются частью интерфейса класса — это самая удивительная фича крестов.

В смысле?

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

В той же MS STL mutable присутствует только в тестах, в самой либе ни одного mutable поля нету,

MS STL в самой либе mutable присутсвует 25 раз. (Я в VSCode поиском посчитал :))

Вот один пример использования mutable.

https://en.cppreference.com/w/cpp/memory/synchronized_pool_resource

class synchronized_pool_resource : public unsynchronized_pool_resource {
    public:
        using unsynchronized_pool_resource::unsynchronized_pool_resource;

        void release() noexcept /* strengthened */ {
            lock_guard<mutex> _Guard{_Mtx};
            this->unsynchronized_pool_resource::release();
        }

    protected:
        void* do_allocate(const size_t _Bytes, const size_t _Align) override {
            lock_guard<mutex> _Guard{_Mtx};
            return this->unsynchronized_pool_resource::do_allocate(_Bytes, _Align);
        }

        void do_deallocate(void* const _Ptr, const size_t _Bytes, const size_t _Align) override {
            lock_guard<mutex> _Guard{_Mtx};
            this->unsynchronized_pool_resource::do_deallocate(_Ptr, _Bytes, _Align);
        }

    private:
        mutable mutex _Mtx;
    };

https://github.com/microsoft/STL/blob/59a87ccc947ed4ccefd3b87e51b6a7136341c674/stl/inc/memory_resource#L592

Кстати, методы же не помечены const. а mutable есть. Загадка…

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

Самый банальный пример: есть у тебя метод, который что-то там сложно вычисляет (или долго откуда-то получает). По смыслу он константный. В какой-то момент решаешь сделать кеширование - от его наличия или отсутствия логика происходящего и «контракт класса» вообще никак не меняется. Конечно, можно убрать константность у метода (создав проблемы себе и другим пользователям кода), но зачем?

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

+100500

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

А если хочется int + Х, то уже придётся делать свободную функцию. Если нужен доступ к потрохам класса, то без friend никак.

А вот это и в самом деле такой себе костыль, причем доступный только разработчику класса. Я со стороны добавить свою функцию в качестве friend не могу (по понятным причинам, да). Перегрузка операторов как-то работает и ладно. Тут сразу вспоминается operator++(int).

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

Самый банальный пример: есть у тебя метод, который что-то там сложно вычисляет (или долго откуда-то получает). По смыслу он константный. В какой-то момент решаешь сделать кеширование - от его наличия или отсутствия логика происходящего и «контракт класса» вообще никак не меняется. Конечно, можно убрать константность у метода (создав проблемы себе и другим пользователям кода), но зачем?

Без анал-огии не обойтись. У тебя в доме есть чайник. Как правило, чайник соблюдает инвариант: вливая чистую воду получаешь чистый кипяток. Но вот ситуация в доме меняется, и ты решаешь, что до туалета бегать тяжело, а проще поссать в чайник. Естественно, сделать это желательно так, чтобы никто не заметил, что в чайнике была моча.

В жизненной формулировке задачи у подавляющего большинства присутствующих возникнет реакция «да как такая херня тебе вообще могла прийти в голову?». Но если мы перекладываем это на программирование, то получается «enterprise software development techniques».

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

Еще я задам наводящие вопросы от противного: как я могу быть уверенным, что логический const у меня действительно всегда const, а не только по будним дням кроме четверга? Как мне тестировать этот код и зависящий от него код? Как оптимизировать такие «const» объекты? Между прочим, в Си аналогичная проблема привела к тому, что модификатор const не влияет на выполнение оптимизаций кода — потому что, естественно, кто-то его кастанет или уже давно держит не-const указатель.

То есть, по сути такой const значит «пожалуйста, постарайтесь не менять эту переменную», и компилятор тебе может выдать предупреждение, если ты ее изменишь, но ты можешь это предупреждение проигнорировать кастом, примерно как предупреждение о неиспользованной переменной в ядре линя является ошибкой и ты должен явно объявлять ее как unused. Const, как и private/public — это такой коммент в коде, который умеет считывать компилятор, и, подобно прочим статическим анализаторам, компилятор выдает ложные предупреждения или пропускает настоящие ошибки.

Итог — заваленный бесполезными объявлениями код, гарантии контрактов в котором ты все равно будешь проверять вручную. Особенно весело проверять их становится, когда const аргумент функции прямо внутри функции внезапно меняет свое значение из-за каких-то вложенных вызовов. Еще веселее, когда эти вызовы неявны и находятся где-то в операторах/конструкторах.

И дальше твоя аргументация идет в сторону «при помощи const ручная проверка контрактов все-таки проще, чем без const». Но она все равно ручная, это сорта говна, где один сорт воняет чуть больше другого, и еще не ясно какой именно. С таким же успехом можно описать кастомный тип данных вроде typename_const, который будет значить «у этого объекта методы не меняют полей — и вы тоже не пытайтесь их менять». Собственно, в STL такие классы уже давно имеются.

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

Всё просто — этот класс не является константным. Я вообще не вижу причин, по которым он каким-то образом мог бы оказаться константным, кроме «мы будем лепить const куда попало, потому что Мейер так сказал». Но я не крестовик — подчеркиваю это. Данные, которые класс выдает, могут быть const, примерно как vector<const int>, но почему-то никто не пытается облепливать код const vector<const int> — потому что все понимают, что такая двойная константность приведет к чудовищному гемору. Вот не умеют контейнера быть константными в рамках C++, хоть ты тресни, и есть куча ситуаций, когда придется эту двойную константность мучительно убирать, а потом мучительно возвращать обратно.

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

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

А если хочется int + Х, то уже придётся делать свободную функцию. Если нужен доступ к потрохам класса, то без friend никак

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

Хотя я бы здесь сделал акцент на том, что перегрузка операторов — это худшая деталь реализации крестов. И речь даже не про саму перегрузку операторов как абстрактную концепцию, а про конкретную реализацию в крестах. При смешивании типов эта худшесть проявляется в полный рост, потому что проблемой становится ответить на вопрос «что произойдет при int + X?». А если какой-то безумец еще и сделает условную компиляцию (#if/#ifdef), то у тебя появляется шанс отхватить поведение, зависящее от фаз луны, когда линкер выкинет случайные N-1 дубли символов и оставит один понравившийся ему. Это мы потихоньку переходим к тому, что в C++ на самом деле нет никакого private.

Конечно: например, пожертвовать эргономикой или выставить наружу «лишние» внутренности

Что?... Эргономика? C++? Эргономика? Ты серьезно? Ездить на ржавом зэпере, половина передач не работает, тормозить нужно ногами через дырку в полу, но руль низковат, это неэргономично. А тормозить ногами ты будешь с double-free или неинициализированными значениями, которые тебе пояснят, как там спрятаны «лишние внутренности» в крестах.

Ещё один пример - тесты. К слову, в расте последнее решается тем, что public/private работает вне модуля - внутри него всё всем доступно, а значит и (внутримодульные) тесты могут ковыряться во внутренностях как хотят

То есть, ты хочешь мне сказать, что невозможность размещения тестов вне модуля — это какая-то классная фича?

Помню, что ты жаловался на это в какой-то из тем, но, честно говоря, не вижу проблемы. И это так не const работает, а спецификаторы типов: int, const int, volatile int, int& - это всё разные типы. Опять же, это всё не относится напрямую к спецификаторам методов

Я жаловался на шаблонный код, который тоннами генерируется под каждый отдельный модификатор типа. И да, это не проблема атрибутов метода.

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

А ты находишь удобной необходимость копипастить реализации для второго метода или кастовать константы для передачи аргументов во второй метод?

«Соблюдение контрактов на словах» плохо работает

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

Это примерно как писать в комментариях - без проверки компилятором оно быстро устаревает и рассинхронизируется с кодом

Вот здесь полностью согласен: чистый код > хорошие комментарии для грязного кода.

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

Чем эти «левые» функции принципиально отличаются от методов? Просто потому что ты не привык? Их набор заранее известен, их видно из объявления класса - всё как с методами

Если использовать friend как исключение --- да, согласен. Но некоторые кадры назначают целые friend-классы — и вот уже из объявления класса ничего не видно.

поскольку private поля являются частью интерфейса класса — это самая удивительная фича крестов.

В смысле?

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

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

А вот это и в самом деле такой себе костыль, причем доступный только разработчику класса. Я со стороны добавить свою функцию в качестве friend не могу (по понятным причинам, да). Перегрузка операторов как-то работает и ладно. Тут сразу вспоминается operator++(int)

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

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

А вот это и в самом деле такой себе костыль

Костыль, да.

причем доступный только разработчику класса.

Ну а это меня не смущает: ведь снаружи и метод нельзя добавить, так что тут симметрично.

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

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

как я могу быть уверенным, что логический const у меня действительно всегда const, а не только по будним дням кроме четверга?

Никак. Точно так же, как ты не можешь быть уверен, что оператор + действительно складывает, а не умножает или даже форматирует диск. Что характерно - это применимо к любому языку. Не понимаю откуда у тебя желание сводить всё к крайностям (и в дальнейшем к абсурду). Такое чувство, что ты привык на динамике заниматься манки патчингом, но потом захотел скорости и теперь требуешь тех же возможностей в С++. Увы, но так не получится. И ты или смиришься, почитаешь умных книжек, научишься с этим жить (опционально: и станешь «крестовиком»). Или так и будешь страдать и плеваться на форуме.

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

И получить UB, если переменная действительно const.

Всё просто — этот класс не является константным.

Класс, вероятно, не является, а вот метод вполне может. И это удобно, в том числе, при чтении кода. Не все, как ты, видя const начинают страдать.

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

Не помню таких сложностей из времён, когда ещё активно писал на плюсах. Возможно ты что-то делаешь не так. (:

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

как я могу быть уверенным, что логический const у меня действительно всегда const, а не только по будним дням кроме четверга?

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

Однако, другие языки не пытаются делать вид, что подобные гарантии предоставляют. Это общая проблема всего C++ — он представляют собой смешную пародию на ООП, на самом деле оставаясь всё тем же Си. Всё, что я хотел сказать «пространными рассуждениями» — это что концепция const в крестах так же не состоятельна, как в Си, причем, по аналогичным причинам. Модификатор const — это очень старая штука, она была еще в K&R «The C Programming Language» (1978) p.40. Зачем СтраусТруп притащил это говно себе в язык — это вопрос риторический, ответа мы скорее всего никогда не узнаем. Впрочем, может быть сам Страуструп этого ответа не знает.

И получить UB, если переменная действительно const

Разве C++ не создан для «UB-driven development»? Ты сам же написал рецепт для UB (модифицировать объект в const методе), а теперь отвечаешь мне «но ты получишь UB». Да, об этом я и пишу, и потому это отвратительный антипаттерн, который популярен среди крестовиков.

Всё просто — этот класс не является константным.

Класс, вероятно, не является, а вот метод вполне может. И это удобно, в том числе, при чтении кода. Не все, как ты, видя const начинают страдать

/*const */MyClass a;

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

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

Не помню таких сложностей из времён, когда ещё активно писал на плюсах. Возможно ты что-то делаешь не так

М-м-м... ты модифицировал константные объекты? Сорян, я не настоящий крестовик, потому не знаком с «элегантными решениями» сей проблемы.

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

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

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

Да, перегрузка операторов местами костыльная и без неё у friend было бы меньше применений. Но у тебя всё сводится к тому, что если что-то не идеально (а зачастую даже просто «непривычно»), то давайте это выкинем и заменим соглашением. Я ж говорю: подход типичного динамиста с желанием всё похакать расширить.

Что?… Эргономика? C++? Эргономика? Ты серьезно?

Вот уж не думал, что я плюсы защищать буду, но ты нагло передёргиваешь, если хочешь сказать, что писать a.add(b).add(c) так же удобно как a + b + c.

То есть, ты хочешь мне сказать, что невозможность размещения тестов вне модуля — это какая-то классная фича?

И где я такое говорил?.. Ты можешь иметь тесты внутри модуля, можешь снаружи. Традиционно внутри пишут юнит-тесты, снаружи - интеграционные.

Но вообще ты должен радоваться: С++ (с friend) как раз позволит тебе организовать тесты «более гибко». Всё, как ты любишь.

А ты находишь удобной необходимость копипастить реализации для второго метода или кастовать константы для передачи аргументов во второй метод?

Я нахожу, что делать это приходится крайне редко.

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

Разве что в плохом коде.

Но некоторые кадры назначают целые friend-классы — и вот уже из объявления класса ничего не видно.

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

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

А… в этом смысле. Ну с этим я привык жить.

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

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

Этот вопрос сводится к «как мне добавить к стороннему классу метод, который принимает мой класс».

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

Это общая проблема всего C++…

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

Да, извини за «токсичность», но сдержаться сложно.

но ты можешь это предупреждение проигнорировать кастом

И получить UB, если переменная действительно const

Ты сам же написал рецепт для UB (модифицировать объект в const методе)

Ну нет, такого я не писал. Если переменная mutable (а речь изначально шла об этом), то каст не нужен и соответственно UB тоже нет.

Кстати, даже с кастом это не UB, если объект «на самом деле» не константный. (:

И вся проблема решена.

Молодец. А теперь реши проблему 100500 методов, куда передаётся const MyClass&. Или твоё решение сводится к тому, чтобы никогда и нигде не писать const? Жалко, наверное, что множество кода ему не следуют.

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

Это ты ситуации когда надо иметь X& getX() и X& getX() const раздуваешь слона?.. Ответ - зависит от. В этом случае использовать const_cast в неконстантной функции легально, если очень хочется. Хотя я думаю, что ты и сам это знаешь, а саму «проблему» подсмотрел у Майерса и пришёл в ужас.

М-м-м… ты модифицировал константные объекты?

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

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

Да, перегрузка операторов местами костыльная и без неё у friend было бы меньше применений. Но у тебя всё сводится к тому, что если что-то не идеально (а зачастую даже просто «непривычно»), то давайте это выкинем и заменим соглашением

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

Вот уж не думал, что я плюсы защищать буду, но ты нагло передёргиваешь, если хочешь сказать, что писать a.add(b).add(c) так же удобно как a + b + c

add(a, add(b, c)) же. Причем, здесь появляется пространтво для обработки ошибок. Так-то упомянутая тобой запись еще блевотнее операторов крестов (привет разрабам питона и руби).

То есть, ты хочешь мне сказать, что невозможность размещения тестов вне модуля — это какая-то классная фича?

И где я такое говорил?.. Ты можешь иметь тесты внутри модуля, можешь снаружи. Традиционно внутри пишут юнит-тесты, снаружи - интеграционные

Тесты модуля, естественно — какое мне дело до остальных сущеностей в проекте, если я о них даже не упоминал?

А ты находишь удобной необходимость копипастить реализации для второго метода или кастовать константы для передачи аргументов во второй метод?

Я нахожу, что делать это приходится крайне редко

Эм-м-м... ты не пишешь либы, а только их дергаешь? Иначе кто тогда пишет константные методы?

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

Пф-ф-ф, так можно докатиться до «ну код-то тебе виден? Прочитай его целиком и пойми, как работают эти две строчки метода».

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

А… в этом смысле. Ну с этим я привык жить

Мыши плакали, кололись, но с этим привыкли жить.

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

Этот вопрос сводится к «как мне добавить к стороннему классу метод, который принимает мой класс»

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

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

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

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

Да, извини за «токсичность», но сдержаться сложно

Вообще не почувствовал, слабо стараешься.

Ну нет, такого я не писал. Если переменная mutable (а речь изначально шла об этом), то каст не нужен и соответственно UB тоже нет.
Кстати, даже с кастом это не UB, если объект «на самом деле» не константный

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

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

По уму, компилятор должен сам определять, что там у тебя в объекте const и что не const, предупреждать, если в данном контексте ты модифицируешь const член класса, а при вызове метода прокручивать агрегированные структуры данных и выдавать их с другими модификаторами. Тогда это настоящие гарантии, тогда на них можно полагаться, а не как на «удобные» машиночитаемые комментарии. (ниже я делаю второй подход к аргументации)

Второй симптом одержимости агрегатными объектами мы тоже обсудили — это public/private, которые опять же являются комментариями и возникли из-за того, что в один объект начали совать совершенно разнородные сущности, и в какой-то момент это настолько стало нормой, что уже и не считается проблемой, хотя об эти грабли по прежнему стукаются с завидной регулярностью, когда возникает потребность получить доступ к тому, что автор сторонней либы посчитал недоступным пользователю. Как я уже написал ранее, самый грамотный подход, требующий минимальных телодвижений — это сделать отдельный метод, который вываливает все кишки наружу, «это как бы, ну, на этом наши полномочия всё».

А теперь реши проблему 100500 методов, куда передаётся const MyClass&. Или твоё решение сводится к тому, чтобы никогда и нигде не писать const? Жалко, наверное, что множество кода ему не следуют

Если метод принимает const MyClass& и при этом делает уберсложные операции внутри, которые потенциально даже непрямо изменят значение const MyClass& — то ССЗБ, чо. Как я уже написал, «константно, потому что Мейерс так завещал».

Давай раскроем, зачем нужен const MyClass& arg. Я как-нибудь сам смогу убедиться, что не вызову очевидный arg.x = 1. Потому на самом деле const нужен для того, чтобы я случайно не вызвал неконстантный метод arg. Проблема в том, что если arg является агрегирующим объектом, то этой гарантии мы лишаемся, потому что const методы будут менять объект. Конечно же на самом деле мне нужно не просто const/non-const, а некие более тонкие уровни, вроде «вызов не блокирует объект», «вызов читает значение мимо кэша», которые по класссической крестовой методологии возможно сделать ТОЛЬКО на уровне контрактов и ручной их проверки.

Я предложил выше другой подход — это разобрать агрегаты на отдельные части и гарантировать бинарный const отдельно на блокировке, отдельно на кэше, отдельно на данных. Это прежде всего подразумевает, что ты наконец оторвешь методы-интерфейсы от данных и начнешь ими оперировать отдельно, как это должно быть, если мы наконец перестанем пародировать ООП, которого в C++ на самом деле нет, поскольку инкапсуляция течёт где только можно, а позднего связывания и передачи сообщений никогда не было. Не путать парадигму языка с тем, что написано на самом языке, поскольку написать можно хоть лисп на ассемблере — это не делает асм функциональным языком с GC.

В этом случае использовать const_cast в неконстантной функции легально, если очень хочется. Хотя я думаю, что ты и сам это знаешь, а саму «проблему» подсмотрел у Майерса и пришёл в ужас

Мейерс как раз не видит в этом проблемы, и предлагает именно твое решение. У меня к таким авторам неизменно возникает один и тот же вопрос: они когда-нибудь в своей жизни писали хотя бы одну программу больше пары тысяч строк? Как правило, большинство гуру примерно на этой отметке останавливаются, потому что примерно на таких объемах теряется способность досконально помнить каждую буковку кода и начинается настоящее програмирование. В которое они не умеют, но учат других.

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

Это ты ситуации когда надо иметь X& getX() и X& getX() const раздуваешь слона?.. Ответ - зависит от. В этом случае использовать const_cast в неконстантной функции легально, если очень хочется.

М-м-м… ты модифицировал константные объекты?

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

Ну это как бы второй вариант, наоборот, дергать неконстантный метод из константного.

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

add(a, add(b, c)) же. Причем, здесь появляется пространтво для обработки ошибок.

По моему, такой вариант ещё хуже. С операторами красивее, при условии, что нас устраивает обработка ошибок на исключениях.

Так-то упомянутая тобой запись еще блевотнее операторов крестов

Почему? К слову, на плюсах можно все три варианта изобразить.

Тесты модуля, естественно — какое мне дело до остальных сущеностей в проекте, если я о них даже не упоминал?

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

А ты находишь удобной необходимость копипастить реализации для второго метода или кастовать константы для передачи аргументов во второй метод?

Я нахожу, что делать это приходится крайне редко

Эм-м-м… ты не пишешь либы, а только их дергаешь? Иначе кто тогда пишет константные методы?

Редко приходится «кастовать константы», а как следствие - писать такие методы, которые отличаются только константностью.

Пф-ф-ф, так можно докатиться до «ну код-то тебе виден? Прочитай его целиком и пойми, как работают эти две строчки метода».

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

Мыши плакали, кололись, но с этим привыкли жить.

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

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

Этот вопрос сводится к «зачем вообще мне нужно хотеть это сделать?».

Так и я о том же.

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

Не заметил, где я бы это говорил.

В целом язык накладывает определённые ограничения. И «поменять всё» ты не сможешь «даже» в питоне. Опять же, большая часть гибкости имеет свою цену в рантайме. Если тебя это устраивает, то зачем тебе С++? А если нет, ну извини - чудес не будет.

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

Вообще не почувствовал, слабо стараешься.

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

И всё это ты будешь проверять руками, как мой любимый strict aliasing

Как бы да, но не совсем. В этом несчастном примере каст производится в неконстантном методе, то есть ты заведомо знаешь, что объект (this) не константный. То есть «проверять руками» - это буквально «посмотреть на строчку выше». В других случаях это действительно костыль/хак и его использование оправдано (зачастую временно!) разве что тем, что «иначе очень много переписывать».

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

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

Честно говоря, я не понимаю, что ты предлагаешь. Звучит это как «сделайте так чтобы всё удобно и хорошо работало». Очень похоже на «аргументацию» недовольных лайфтаймами в расте - мол некрасиво и многословно, надо иначе. Вот только решения никто не предлагает. И да, я дочитал твоё сообщение до конца, но решение или не понял или оно вовсе не решение.

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

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

Не могу тут с тобой согласиться. По моему, вместо не идеального, но рабочего решения ты предлагаешь вообще от него отказаться. Выглядит как потеря в удобстве. Конечно, ты возразишь, что «бороться с private и const» - это не удобство. В первую очередь, мы расходимся во мнение, что с этим приходится бороться. Да, у меня тоже бывали случаи когда библиотечный код не давал возможности расширить его под мои потребности, но (в моей практике) происходило это совсем не так часто, как ты рассказываешь. То есть, гораздо чаще всё «работает как надо» и приносит больше выгоды, чем проблем. С константностью так вообще ощутимых проблем не припоминаю.

Я как-нибудь сам смогу убедиться, что не вызову очевидный arg.x = 1

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

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

Уточни о чём речь.

У меня к таким авторам неизменно возникает один и тот же вопрос: они когда-нибудь в своей жизни писали хотя бы одну программу больше пары тысяч строк?

И что же в этом конкретном случае меняется в кодовой базе после этого предела?

Ну это как бы второй вариант, наоборот, дергать неконстантный метод из константного.

Не понял тебя. Возможно, потерял контекст.

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

add(a, add(b, c)) же. Причем, здесь появляется пространтво для обработки ошибок.

По моему, такой вариант ещё хуже. С операторами красивее, при условии, что нас устраивает обработка ошибок на исключениях

Ну или add(a, b, c) аля лишп.

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

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

Так-то упомянутая тобой запись еще блевотнее операторов крестов

Почему? К слову, на плюсах можно все три варианта изобразить

Но изображают почему-то в основном одну.

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

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

Редко приходится «кастовать константы», а как следствие - писать такие методы, которые отличаются только константностью

Вот это странные показания. Ты хочешь сказать, что модификаторы константности сторонних либ тебя вполне устраивают? Своих либ ты не пишешь?

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

В целом язык накладывает определённые ограничения. И «поменять всё» ты не сможешь «даже» в питоне. Опять же, большая часть гибкости имеет свою цену в рантайме

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

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

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

Честно говоря, я не понимаю, что ты предлагаешь. Звучит это как «сделайте так чтобы всё удобно и хорошо работало». Очень похоже на «аргументацию» недовольных лайфтаймами в расте - мол некрасиво и многословно, надо иначе

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

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

Хм-м-м, еще один звоночек. Значит, ты все-таки в основном писал клей под готовые либы? Потому что даже с самым-самым проверенным сторонним решением неизбежно возникает потребность расширить его больше, чем расчитывал самый привередливый разработчик.

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

Зачем? Крестовый const дает только гарантию, что ты не сможешь поменять значение объекта, но не дает никаких гарантий по поводу того, что значение не изменится при выполнении твоей функции. Вот вообще никаких, и с этим в том числе компиляторы смирились, «const T&» для них равнозначно «T*».

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

Уточни о чём речь

Где-то в недрах кто-то чей-то метод дернул и объект поменялся. Проверить руками, меняется ли объект или нет, можно, но чудовищно трудоемко.

И что же в этом конкретном случае меняется в кодовой базе после этого предела?

Не в кодовой базе, а меняется методика работы с кодовой базой. То есть, с восприятия манагера «короткие заученные алгоритмы, помню каждую строчку, одну функцию пишу за две минуты» нужно перестроиться на восприятие программиста «какая у нас общая картина, какие функции мне нужно вспомнить-прочитать, чтобы написать новую функцию; как мне написать новую функцию так, чтобы через год я понял, о чем она; как мне написать новую функцию в минимальное число логических сущностей, чтобы потому через год мне потребовалось меньше времени на прочтение; как сделать так, чтобы новая функция работало независимо от треша, который происходит в коде, написанном не мной (или мной, но на большом временном удалении)».

Ну это как бы второй вариант, наоборот, дергать неконстантный метод из константного.

Не понял тебя. Возможно, потерял контекст

Это было про дублирующие методы const/non-const. В основном людишки предпочитают дергать const метод из non-const.

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

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

Вот это тоже не очень понимаю. С RAII всё «работает само».

К слову, ты любишь говорить о проблемах RAII и исключениях в деструкторах. В моей практике, это проблема скорее теоретическая. То есть, да - может выстрелить и иногда подкладывают соломку в виде try/catch в деструкторе, но реально это случается крайне редко. Точно так же какой-нибудь псих может в либе сделать throw 1 или позвать terminate, но в 99% случаев так не делают. Но да, гарантий нет: можно плакать, что всё пропало, а можно жить с этим. Прямо как с паникой в расте. (:

Вот это странные показания. Ты хочешь сказать, что модификаторы константности сторонних либ тебя вполне устраивают? Своих либ ты не пишешь?.

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

Кстати, в расте, на котором я теперь пишу, свой аналог mutable имеется (RefCell), правда const_cast (вместе с protected и friend) не завезли. Отсюда правда можно разные выводы сделать. Или, что данная фича задизайнена нормально и кардинально улучшить не получается. Или что раст делали «больные плюсовики», а у меня синдром утёнка.

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

Хм-м-м, еще один звоночек. Значит, ты все-таки в основном писал клей под готовые либы? Потому что даже с самым-самым проверенным сторонним решением неизбежно возникает потребность расширить его больше, чем расчитывал самый привередливый разработчик.

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

Возвращаясь к твоему вопросу: сейчас есть под рукой проект в котором 2.2 миллиона строк кода на С++ (так-то там целый зоопарк, но плюсы доминируют). Когда я над ним (в течении примерно трёх лет) работал, то было поменьше, но всё ещё больше миллиона. Не знаю бывает ли «клей» в таком объёме, но в данном случае это не так. Туда входит и UI на Qt, но большая часть это своя логика, структуры данных и т.д. Впрочем, так любимый тобой буст, используется.

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

Зачем?

Даже не знаю, что ответить. Мне это кажется естественным: хотеть знать будет ли изменён объект после того как я передам его в какую-то функцию. Хотя в других языках как-то без этого живут, но мне оно кажется странным. Наверное, это опять «просто не делай a.b = 1 (там где не надо) против желания иметь «страховку» (если и не гарантии)».

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

Ты уже о другой проблеме говоришь.

Где-то в недрах кто-то чей-то метод дернул и объект поменялся. Проверить руками, меняется ли объект или нет, можно, но чудовищно трудоемко.

Всё ещё не пойму. Вот есть у нас class A { B b; };. А - это агрегирующий объект? И что может поменяться, если мы внутри foo(const A& a)? Кто-то, имеющий (не константную) ссылку на А, в другом потоке поменяет?

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

Вот это тоже не очень понимаю. С RAII всё «работает само»

Вот ты снова звучишь как пользователь либ. Авторы твоих либ трахались годами для того, чтобы натянуть свою поделку на стэковую модель RAII, и натянули таки после некоторых итераций, а потом приходишь ты и такой «всё работает само». Например:

https://github.com/qt/qtbase/blob/dev/src/widgets/kernel/qwidget.cpp#L1414

Модель RAII подразумевает строго стэковую модель данных — онная совпадает со стэковой моделью данных вызовов функций, потому на «домашних заданиях» проблема не заметна. Часто получается, что «фарш нельзя провернуть назад», и высвобождение должно производиться в том же порядке, что инициализация. Например, односвязанный список. И такой случай успешно обрабатывает RAII через один большой линейный деструктор.

Проблемы начинаются, когда не прокатывает ни стэковая модель, ни «большая функция, отменяющая эффекты иницилазации». А именно — большинство случаев с более-менее сложными побочными эффектами, которые нельзя просто освободить в стэковом режиме так, будто ничего не произошло. Хотя бы некий асинхронный вызов, передача сообщений, etc — то, чего в C++ нет, и в частности потому C++ не поддерживает ООП. Даже банальная работа с сетью — нельзя «отозвать» пакет данных. Или какая-то ячейка данных оказалась сохраненна в глобальной кучу, какую-то нужно высвободить, и нужно прежде всего разобраться, что попало куда. Причем, высовобождение одной ячейки может посыпать работу зависящих ячеек, которые были выделены ранее (аля Inversion of Control).

Одно из неявных следствий — фундаментальная однопоточность крестов. Весь язык построен из одного потока выполнения, один поток выделения-высвобождения на стэке, и даже в многопоточной разработке применяют стэковое взятие блокировок — хотя блокировки являются далеко не единственным средством многозадачности. Или даже так: блокировки являются хорошим средством для того, чтобы лишить систему многозадачности. Один деятель IT древности даже предлагал заменить название Semaphore на Bottleneck. Ну типа bottleneck.enter()/bottleneck.leave() — это бы более точно отражало сущность семафоров.

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

В плане константности руководствовался общими практиками и боли не чувствовал

У едоков кактусов какой-то странный болевой порог. Мне вот длительная компиляция создает боль в анусе, но крестовикам-растаманам норм. Большинство крестовых и растовых либ мне кажутся нечитаемой лапшой из сплошных вспомогательных объявлений. Мне просто интересно: сами крестовики вообще читают STL?

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

Я уже привел более-менее знакомый всем пример — это итераторы в STL. Тебя не смущает наличие const_iterator? Или это вроде типа тоже «общие практики»?

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

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

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

Впрочем, так любимый тобой буст, используется

Буст бусту рознь, это большое собрание самых разных либ.

Мне это кажется естественным: хотеть знать будет ли изменён объект после того как я передам его в какую-то функцию

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

Ты уже о другой проблеме говоришь.

Хотеть можно много чего, но знать ты это сможешь только прочитав каждую строчку кода. Ты-то передал объект с «const», но функция дернула метод, который через глобальные ссылки поменял «константный» объект. Есть языки, которые гарантию константности могут давать, но кресты в число этих языков не входят. То есть, опять-таки, крестовая «гарантия» находится на уровне других статических анализаторов кода, которые иногда дают ложные ошибки, иногда упускают настоящие ошибки, и до настоящих гарантий там как до киева раком.

И здесь проблема даже не в константных модификаторах самих по себе, а в совершенно необузданном неявном поведении, которое настолько бесит кодеров, что они даже сделали язык Zig, построенный на отсутствии неявного поведения. Именно по этой причине запись «add(a, b)» для сложных сущностей намного лучше записи «a + b» — ты не ждешь подлянки от плюса, а она придет именно от него. Если же ты видишь явную функцию «add», то понимаешь, что там может произойти что-то нехорошее. Но самое мерзкое неявное поведение, конечно же, происходит от копирующих-кастующих конструкторов.

И конкретно C++ завис в непонятной нише: с одной стороны, вроде как высокоуровневый язык с полуавтоматическим управлением памятью, с возможностью наращивать абстракции, но с другой стороны абстракции текут как самка в брачный период, полуавтоматика требует кучи ручного управления, чтобы не выдать очередное UB, которое в крестах получить удивительно легко. Ни рыба, ни мясо, но «пипл хавает». Почему я и пишу, что нужно четко разделять языки и подходы для высокоуровневого программирования с системным программированием. При этом системному программированию не нужны опасные уровни абстракций, ему нужно просто подобие Си, но с грамотной архитектурой, с модулями, с передачей обобщенных блоков кода аргументами (аналог передачи блоков в макросы), с процедурным RAII, и с constexpr/consteval, чтобы наконец избавиться от предварительно генерируемых портянок массивов/таблиц — то, чем мог бы быть раст, но по итогу не смог.

Вот есть у нас class A { B b; };. А - это агрегирующий объект? И что может поменяться, если мы внутри foo(const A& a)? Кто-то, имеющий (не константную) ссылку на А, в другом потоке поменяет?

Что такое foo? Если это чистая функция, которая не видит никаких других переменных, кроме аргумента — ты прав, A не поменяется, если спецом не пытаться сломать программу. Самый-присамый каноничный пример проблемы — оператор присовоения самому себе. У него ведь тоже «const T&», но при этом он сразу же свой аргумент меняет, и потому надо обязательно это дело поднимать костылем из &arg != this.

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

Вот ты снова звучишь как пользователь либ.

А мне кажется, что ты снова выдаёшь желаемое за действительное.

Qt вообще не очень удачный пример с их deleteLater и прочим. Да, сложные штуки делаются сложно. Но дело даже не в этом: сначала ты говоришь, что с исключениями не можешь жить без GC: память надо собирать - RAII эту задачу решает. А теперь начинаешь рассказывать ужасы про «отзывание пакетов» c чем GC никак не поможет..

Весь язык построен из одного потока выполнения, один поток выделения-высвобождения на стэке

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

У едоков кактусов какой-то странный болевой порог. Мне вот длительная компиляция создает боль в анусе, но крестовикам-растаманам норм.

Опять же, у тебя выборочная слепота. Нет совершенно ничего удивительного, что люди привыкают. Человек, который «всю жизнь» на крестах писал смирился с долгой компиляцией. И если ты ему предложишь условный питон в качестве решения, то он будет плеваться от динамической типизации и тормозов в рантайме. Вот ты ведёшь себя точно так же, только с другой стороны. А если всё-таки видишь, что идеала нет, то хорошо - стал на шаг ближе к дзену. Правда можно скрежетать зубами, можно «расслабиться и получать удовольствие» (выбрать наиболее подходящий компромисс), можно пытаться изменить ситуацию. Первое проще всего, последнее - сложнее.

Я уже привел более-менее знакомый всем пример — это итераторы в STL. Тебя не смущает наличие const_iterator?

Не очень. Если на то пошло, то Iter/IterMut в расте меня смущают больше.

Что конкретно напрягает тебя? Необходимость два раза «реализовывать» «одно и то же»?

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

Буст бусту рознь, это большое собрание самых разных либ.

Да, но если я ничего не путаю, то ты как-то писал, что нормальные проекты буст использовать не будут. (:

Ты-то передал объект с «const», но функция дернула метод, который через глобальные ссылки поменял «константный» объект.

Предпочту держаться от такого кода подальше.

Мне не интересно спорить про «настоящие гарантии» потому как всё можно довести до абсурда и так окажется, что никаких гарантий нет нигде. И джава может упасть с сегфолтом и даже в языках с доказательствами есть «хаки». Можно, конечно, доверять коду только просмотрев каждую строчку (вплоть до компилятора!), но в подавляющем большинстве случаев это не практично. Главное, что я не согласен с возможным выводом, что С и какой-нибудь Idris предоставляют одинаковые гарантии, при этом у тебя прослеживается именно этот аргумент.

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

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

Самый-присамый каноничный пример проблемы — оператор присовоения самому себе. У него ведь тоже «const T&», но при этом он сразу же свой аргумент меняет, и потому надо обязательно это дело поднимать костылем из &arg != this.

Формально ты может и прав, но этот «костыль» используют не для того, чтобы всё не ломалось, а для оптимизации. По крайней мере, в нормальном коде.

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

Но дело даже не в этом: сначала ты говоришь, что с исключениями не можешь жить без GC: память надо собирать - RAII эту задачу решает

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

А теперь начинаешь рассказывать ужасы про «отзывание пакетов» c чем GC никак не поможет

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

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

Это и есть однозадачность. То есть, даже если в одном процессе крутится несколько потоков — они не имеют общих данных, они взаимодействуют как независимые процессы, аля Erlang. Чтобы была многозадачность — нужно иметь хотя бы общий ресурс «канал связи». И да, я понимаю, что в операционных система уже есть костыли, которые позволяют развязать вход и выход этого канала так, чтобы два процесса не конфликтовали за них.

Человек, который «всю жизнь» на крестах писал смирился с долгой компиляцией. И если ты ему предложишь условный питон в качестве решения, то он будет плеваться от динамической типизации и тормозов в рантайме. Вот ты ведёшь себя точно так же, только с другой стороны

С какой «другой»? Ты серьезно считаешь меня питонистом? Я — тот самый человек, который восемь лет проработал на проекте, где эдак 3-4 года жрали кактусы, компилируя по часу проект. Потом меня это дело задостало, я взял и сократил время компиляции до 5 минут — и разработка стала совершенно иной. Мне кажется, что меня даже за это дело начали недолюбливать, потому что раньше «я занят, компилирую проект», а потом внезапно «заняться нечем».

Я как раз самый неугомонный и гибкий: вчера писал SPA на JS, сегодня пишу http-балансеры на сихе.

А если всё-таки видишь, что идеала нет, то хорошо - стал на шаг ближе к дзену

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

Я уже привел более-менее знакомый всем пример — это итераторы в STL. Тебя не смущает наличие const_iterator?

Не очень. Если на то пошло, то Iter/IterMut в расте меня смущают больше

И это тоже. У раста автоматика между функциям (типы, время жизни) — это такая же бида-печаль. Разрабы раста поднялись на следующую ступень по сравнению с auto, но брести им на пятый этаж такими темпами еще очень и очень долго.

Что конкретно напрягает тебя? Необходимость два раза «реализовывать» «одно и то же»?

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

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