LINUX.ORG.RU

юнионы в C++

 


2

4

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

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

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

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

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

★★★★★

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

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

Приведите, пожалуйста, список из 4-5-6 случаев, где это нужно.

Сорьки что без спросу врываюсь в вашу дискуссию. Я полюблял на интервью спрашивать если mutable это просто syntax sugar, или там есть что-то еще? Теоретически же можно cast away constness from a ‘this’ pointer получив на выхлопе ну вот точно такой же asm… Понимание происходящего имело место быть у исчезающе малого круга кандидатов, что безусловно очень печально конечно.

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

Теоретически же можно cast away constness from ‘this’ pointer

Я в первой половине 90-х так и делал :)

Пока в руки не попала какая-то книжица по C++, которая описывала более-менее современную версию C++. И там уже вычитал про mutable.

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

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

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

Точно так же как в Rust-е вы должны забыть как оно в Java, а в Java нужно забыть как оно в Ruby.

Нууу… вот сейчас стало немного смешно. Потому, что в большинстве живых ЯП многие фичи содраны ещё откуда-то. Та же модульность в С++20 — авторы решили таки именно что вспомнить, как оно в других языках. И это, я считаю, хорошо!

(Я даже догадываюсь, что вы на это ответите — что на то они и авторы, что не ноют на ЛОРе, а сами развивают язык.)

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

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

Потому, что в большинстве живых ЯП многие фичи содраны ещё откуда-то

Яркий пример: в Python/Ruby активно используется duck typing. Будете ли вы в Java использовать duck typing так же активно, как в Python/Ruby или таки постараетесь использовать Java-вские интерфейсы?

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

Если вы не осознаете, что в C++ нужно программировать именно как на C++, а будете по привычке применять любимые приемы из Java, то это неизбежно приведет к одному из двух (а то и к двум сразу):

  • вы будете местами терять эффективность;

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

Взять, например, исключения. Если вы пришли из Си или Rust-а, то вам нужно будет научиться пользоваться исключениями. И если вы пользуетесь исключениями в Java, то обеспечение exception safety в С++ и в Java вы будете делать разными способами. Причем, если в C++ вы попробуете оглянуться на опыт из Java, то вряд ли у вас получится что-нибудь путное.

Ну или та же константность. В C++ она вот такая. В Java/Python/Ruby ее вообще нет. В D она другая. Если вы с привычками из C++ попробуете попрограммировать на Java/Python/Ruby, обнаружите, что у вас нет дополнительного контроля со стороны компилятора, который бы в C++. И вам нужно будет менять привычки и, местами, подходы. Аналогично, если вы перейдя с D на C++ захотите поиметь иммутабельные объекты с транзитивной иммутабельностью, то опять столкнетесь с тем, что у вас нет того, к чему привыкли.

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

О, я приведу пример: в Rust компилятор не даст (если это не блок unsafe) скомпилировать программу, в которой он не будет уверен, что переменная создана - если она объявлена и используется. И вроде разумно, но я могу предположить ситуацию когда рабочая схема просто не будет компилироваться и потребует переработку, а с/с++ прекрасно это скомпилирует (это не хорошо и не плохо - это просто есть)

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

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

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

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

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

Это так, но я к тому, что если человек в C++ привык полагаться на перегрузку и писать в стиле:

buf.put(int_field);
buf.put(float_field);
buf.put(string_field);

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

put_int32(buf, int_field);
put_float32(buf, float_field);
put_string(buf, string_field);

так еще и потребуется менять этот код если int_field в один прекрасный момент поменяет свой тип с int32 на uint16 или int64.

И обратно: если перегрузки в языке не было, то после перехода на C++ можно продолжать писать по привычке put_int32(buf, v), а потом обнаружить, что такой код в принципе не дружит с шаблонами…

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

std::vector<const std::string>

Это не работает в С++ вообще.

Никакой контейнер стандартной библиотеки не может иметь тип const T потому что allocator<const T> является ill-formed.

В Visual C++ такой код выдаст ошибку компиляции.

static_assert failed: 'The C++ Standard forbids containers of const elements because allocator<const T> is ill-formed.'

В будущем возможно будут добавлены static_assert в каждый контейнер для вывода более понятного сообщения: https://github.com/microsoft/STL/issues/180

https://timsong-cpp.github.io/cppwp/allocator.requirements#tab:utilities.allocator.requirements

T, U, C denote any cv-unqualified object type

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

так я в принципе про это же, что при переходе с языка на язык или со стиль на стиль, будет жёсткая попа боль. После с++ мыслить traits - сложно, не привычно. Куда проще осознать аналог исключений - Result Option.

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

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

Ну мы здесь все вместе у buko3y это и наблюдаем :)

Куда проще осознать аналог исключений - Result Option.

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

Другое дело, что в языках с АлгТД и паттерн-матчингом (а так же специальным синтаксическим сахаром вроде Rust-овского ?) использовать Result/Option проще и привычнее, чем в C++. Особенно в C++ до C++17.

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

вспоминаем, что в C++20 эти самые модули добавляют…

И лучше те люди, которые за это взялись - этого не делали.

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

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

За такое надо причиндалы отрывать.

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

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

в языках где есть definition module(определения модуля) и implementation module(реализация модуля) перекрестные ссылки между модулями реализации допустимы. в плюсах это есть.

https://www.modernescpp.com/index.php/c-20-module-interface-unit-and-module-implementation-unit

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

https://vk.com/video-155609632_456239126 - здесь Гризмер говорит о влиянии Оберона, но я не осилю дослушать сейчас. Как минимум там в самом начале есть «сначала я учился у Вирта и работал на Обероне, а потом попал на работу на С++ и понял, что всё плохо». Попытаться сделать обратно, чтобы стало нормально - это было мотивом для Гризмера для его участия в проекте go. Хотя активный Оберон тут не при чём.

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

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

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

В C++ вы не можете держать неконстантную ссылку на константный объект без UB

Вот именно. И как мне передать в контейнер ссылку на константный объект? Это будет тип данных, несовместимый с контейнером ссылок на неконстантные объекты. То есть, вектора из констант или смешанных значений в STL запрещены.

У C++ нет проблем с const, это у тех, кто общается с вами есть проблема – ваша упоротость

Так почему при написании shared_ptr разработчики STL отошли от собственного правила «константный контейнер хранит константное значение»?

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

Да тут половину чата может создать собственный ЯП, это не rocket science.

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

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

Кэш, мутекс (для того же RW-lock или conditional variable), счетчик ссылок, отладочная инфа/статистика, ссылки в интрузивном контейнере — хватит?

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

одну и ту же программу можно разными совершенно способами писать на одном и том же C++

Не льстите себе, у вас не получится.

И как мне передать в контейнер ссылку на константный объект?

В какой контейнер?

Это будет тип данных, несовместимый с контейнером ссылок на неконстантные объекты.

Теперь тоже самое, но понятным русским языком.

То есть, вектора из констант или смешанных значений в STL запрещены.

Зачем вам это?

Так почему при написании shared_ptr разработчики STL отошли от собственного правила «константный контейнер хранит константное значение»?

Теперь тоже самое, но понятным русским языком.

Да тут половину чата может создать собственный ЯП, это не rocket science.

Уровня C++, Rust или даже Go?

Не льстите ни себе, ни этому чату.

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

Кэш,

Допустим. Но есть вопрос, зачем кэшу нужно иметь const-методы?

мутекс (для того же RW-lock или conditional variable),

Зачем мутексу это нужно? Т.е. какой смысл в константном mutex-е или зачем кому-то захватывать mutex, полученный по константной ссылке?

счетчик ссылок,

Ok.

отладочная инфа/статистика,

Что?

ссылки в интрузивном контейнере

Что?

хватит?

Пока 1.5. Не хватит.

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

в C++ вы можете задействовать шаблоны, получить тот же duck typing, но только с контролем со стороны компилятора

Это глубокое заблуждение, что duck typing обязательно должен быть без контроля типов. В Go duck typing поддерживает контроль типов. В то же время, на C++ можно написать код на шаблонах, который черт знает какой тип приводит к непонятно чему, из-за чего получить ошибку в рантайме.

Если вы не осознаете, что в C++ нужно программировать именно как на C++, а будете по привычке применять любимые приемы из Java, то это неизбежно приведет к одному из двух (а то и к двум сразу):
вы будете местами терять эффективность;

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

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

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

Ну или та же константность. В C++ она вот такая. В Java/Python/Ruby ее вообще нет. В D она другая. Если вы с привычками из C++ попробуете попрограммировать на Java/Python/Ruby, обнаружите, что у вас нет дополнительного контроля со стороны компилятора, который бы в C++

Внезапно, соблюдение константности через интерфейсные функции и несовместимые типы реализуемо в других ЯП. Ровно как ее можно реализовать (и реализовано кое-где в STL) таким образом и в C++.

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

Но есть вопрос, зачем кэшу нужно иметь const-методы?

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

Зачем мутексу это нужно? Т.е. какой смысл в константном mutex-е или зачем кому-то захватывать mutex, полученный по константной ссылке?

Чтобы получить право изменять значение по ссылке. Или чтобы атомарно прочитать значение в роли consumer-а.

отладочная инфа/статистика,

Что?

Даже в релизных сборках иногда остается, потому что в программах БЫВАЮТ ОШИБКИ, бывает плохая производительность, нужно как-то получать обратную связь из программы на языке, в котором не существует интроспекции.

ссылки в интрузивном контейнере

Что?

Связанный список константных значений. Удалили константу из списка — поменяли ссылки на соседних узлах. Собственно, большинство контейнеров STL испытывают большие трудности в хранении константых значений (inb4: ненужно).

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

Это глубокое заблуждение

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

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

Ну и долбодятел, что тут еще сказать.

Внезапно, соблюдение константности через интерфейсные функции и несовместимые типы реализуемо в других ЯП. Ровно как ее можно реализовать (и реализовано кое-где в STL) таким образом и в C++.

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

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

Потому что изначально в интерфейсе объекта никакого кэша не было.

Ок, засчитаем как и подсчет ссылок.

Чтобы получить право изменять значение по ссылке. Или чтобы атомарно прочитать значение в роли consumer-а.

По какой ссылке? Речь про мутекс. Какой смысл в захвате/освобождении константного мутекса?

Даже в релизных сборках иногда остается, потому что в программах БЫВАЮТ ОШИБКИ, бывает плохая производительность, нужно как-то получать обратную связь из программы на языке, в котором не существует интроспекции.

Какое отношение все это имеет отношение к константности?

Связанный список константных значений. Удалили константу из списка — поменяли ссылки на соседних узлах.

Что? Если у вас константный контейнер (константная ссылка на контейнер), то как вы из него собрались что-то удалять?

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

Никакой контейнер стандартной библиотеки не может иметь тип const T потому что allocator<const T> является ill-formed

Может, но только до тех пор, пока ты не пытаешься засунуть в него значение, то есть, делать new/move/copy/delete. Нет никакой теоретической проблемы в том, чтобы иметь изменяемый контейнер констант — проблема возникает только для конкретных контейнеров конкретного STL на конкретном языке.

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

И как мне передать в контейнер ссылку на константный объект?

В какой контейнер?

map, set, vector, list.

Это будет тип данных, несовместимый с контейнером ссылок на неконстантные объекты.

Теперь тоже самое, но понятным русским языком.

const std::vector<const char*> и const std::vector<char*> являются двумя ВЗАИМНО несовместимыми типами.

То есть, вектора из констант или смешанных значений в STL запрещены.

Зачем вам это?

«НИНУЖНА!»

Так почему при написании shared_ptr разработчики STL отошли от собственного правила «константный контейнер хранит константное значение»?

Теперь тоже самое, но понятным русским языком.

юнионы в C++ (комментарий)
https://godbolt.org/z/Krc73v5Te

Я меняю значение в константном объекте, не используя ни mutable, ни const_cast. Мистика...

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

морковкин, константа это не значение, япона мать.

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

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

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

По какой ссылке? Речь про мутекс. Какой смысл в захвате/освобождении константного мутекса?

У объекта есть мутекс, так? Но поля константного объекта — константны, так? Значит, хранящийся в объекте мутекс тоже константный. Но смысла в константном мутексе нет. Значит, мы должны делать static_const или mutable. Но можно поступить еще проще — никогда не применять модификатор const в сложных объектах. Почеркиваю слово «сложных».

Какое отношение все это имеет отношение к константности?

Собирать изменяемую статистику нужно для всех объектов, и для изменяемых, и для константных. Можно, конечно, для каждого объекта выделять независимый блок памяти, который ссылкой связывать с константной областью памяти — примерно так делает STL для отдельных контейнеров (list, unordered_map/set, shared_ptr).

Если у вас константный контейнер (константная ссылка на контейнер), то как вы из него собрались что-то удалять?

Неа, читай еще раз: «Связанный список константных значений». Это не константный контейнер — порядок слов другой, из-за чего смысл может меняться.

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

map, set, vector, list.

Кладите туда const T* или reference_wrapper<const T>.

const std::vector<const char*> и const std::vector<char*> являются двумя ВЗАИМНО несовместимыми типами.

Именно так. Это особенность C++, вполне себе логичная. В чем проблема?

«НИНУЖНА!»

Ожидаемо слились.

Я меняю значение в константном объекте, не используя ни mutable, ни const_cast. Мистика…

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

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

Значит, хранящийся в объекте мутекс тоже константный.

если мьютекс есть часть обьекта, и меняют его состояние, разумеется обьект неконстантен по определению…

кстати нафига константному обьекту мьютекс? такой обьект возвращает одно и тоже, откуда его ни читай.

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

У объекта есть мутекс, так?

Стоп, здесь явно есть непонимание предмета. Правильно ли я понимаю, что речь про:

class D {
  mutex lock_;
public:
  void f() {
    lock_guard lock{lock_}
    ...
  }
};

Если речь про такой сценарий, то OK, в туже кассу, что и кэш, и счетчик ссылок.

Пока всего 3.

Причем все три отлично покрываются C++ным mutable. Непонятно какие претензии к константности в C++.

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

Что? При чем здесь константность?

Неа, читай еще раз: «Связанный список константных значений»

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

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

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

Даже std::string является ссылкой на память. И оно может быть ссылкой на константную память. Но позиция авторов стандартной либы «вам это не нужно». Я привел более понятный пример char */const char*, для которого уже не прокатывает отмаза «ненужно» — так очень даже нужно и можно, это работает, это компилируется, но это неудобно и опасно.

потому создавай контейнер значений-неконстант, пиши туда константы и не мучай ж

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

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

«НИНУЖНА!»

Ожидаемо слились

А что я могу сделать? У крестовика насрано в комнате, я говорю «нужно убрать», а он мне «не нужно! здесь так принято, привыкай». Ну ок.

const std::vector<const char*> и const std::vector<char*> являются двумя ВЗАИМНО несовместимыми типами.

Именно так. Это особенность C++, вполне себе логичная. В чем проблема?

В том, что в программе бывают константные и неконстантные значения, и порой приходится применять их одновременно. А иначе зачем мне вообще ваш const.

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

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

void tryChange(const Container& item) {
    *item.b = std::string("Bitch");
}
byko3y ★★★★
()
Ответ на: комментарий от alysnix

кстати нафига константному обьекту мьютекс? такой обьект возвращает одно и тоже, откуда его ни читай

Как ты думаешь, если у меня функция получает const SomeType &object — объект может поменяться между вызовами функции? Он же константный. А вот потому что константность ссылки ничего не говорит про константность объекта, на который ссылаются.

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

Причем все три отлично покрываются C++ным mutable. Непонятно какие претензии к константности в C++

Пф-ф-ф, можно вообще все поля у всех объектов сделать mutable — очень удобно будет, никакого static_const не понадобится. Но можно сделать намного проще — не использовать const изначально.

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

Что? При чем здесь константность?

В константном объекте хранятся неконстантные счетчики/строки.

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

Нет, это я спрашиваю «как?».

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

А что я могу сделать?

Ответить на вопрос.

У крестовика насрано в комнате

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

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

И? В чем проблема?

Но вот же ж константная ссылка, или меня уже глючит?

Так вы же сам объект item не меняете.

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

Пф-ф-ф, можно вообще все поля у всех объектов сделать mutable — очень удобно будет

Кому будет удобно?

Но можно сделать намного проще — не использовать const изначально.

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

В константном объекте хранятся неконстантные счетчики/строки.

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

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

Но тогда все еще непонятно какие претензии к C++ному const-у.

Нет, это я спрашиваю «как?».

Вы спрашиваете «как сделать лютую херню?»

Никак. Не нужно делать лютой херни.

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

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

*item.b = std::string(«Bitch»);

это значит - изменить то, на что указывает указатель item.b. то есть сам item не меняется.

это все равно что

auto lp = item.b;
*lp = std::string(...);
alysnix ★★★
()
Ответ на: комментарий от byko3y

Нет, это я спрашиваю «как?».

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

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

в языках где есть definition module(определения модуля) и implementation module(реализация модуля) перекрестные ссылки между модулями реализации допустимы. в плюсах это есть.

между модулями реализации

реализации

Это было и до модулей.

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

Но его нет и не будет.

Ибо «это сложно». Сложно не значит невозможно.

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

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

Какое отношение строки-массивы имеют к дополнительным блокам памяти?

https://gcc.gnu.org/onlinedocs/gcc-12.1.0/libstdc++/api/a00362_source.html

Но в unordered_map/unordered_set/unordered_multiset/unordered_multimap двойные указатели

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

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

Память – частный случай ресурса. Ресурс освобождается в деструкторе. GC позволяет не париться о высвобождении памяти после исключения, но все сказанное про деструкторы, в том числе и взаимная рекурсивность, применимо и к финализаторам. Утечка ресурсов, отличных от памяти, ничем не отличается от утечки памяти, а GC от нее никак не защитит.

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

…кто будет проверять, что в union лежит корректное значение, кто будет проверять, что…

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

Это не работает в С++ вообще.

Никакой контейнер стандартной библиотеки не может иметь тип const T потому что allocator является ill-formed.

Делов то:

#include <iostream>
#include <list>
#include <string>
#include <vector>

struct S {
   explicit S(const std::string& str): str_(str) {}
   const std::string str_;
};

int main()
{
   typedef std::list<S> StrList;
   StrList strList;
   strList.emplace_back("aaa");

   typedef std::vector<S> StrVec;
   StrVec strVec;
   strVec.emplace_back("bbb");
   strVec.emplace_back("ccc");

   std::cout << strList.size() << ' ' << strVec.size() << std::endl;
   return 0;
}

Вы же этого хотели добиться (@byko3y)? Я уверен есть стандартный wrapper template позволяющий сделать то же самое в обобщённом виде, если нет - пишется за 5 минут.

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

проблема возникает только для конкретных контейнеров конкретного STL на конкретном языке.

Я сейчас проверил и узнал что libstdc++(gcc) тоже запрещает контейнеры с элементами константного типа.

https://gcc.godbolt.org/z/51MWhsKKj

Спасибо что своим упрямством заставил меня отправить патч в LLVM :)

https://reviews.llvm.org/D125518

Update: Я закрыл свой патч так как увидел этот коммит: https://github.com/llvm/llvm-project/commit/a54d028895c91da356a4aaf30e27a5a5b90dd313

but it caused widespread breakage at Google (which suggests it would cause similar breakage in the wild too), so now I’m reverting again.

в Google работают люди со схожим взглядом на const как у тебя :)

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

Я уверен есть стандартный wrapper template позволяющий сделать то же самое в обобщённом виде, если нет - пишется за 5 минут.

Ну вообще, в STL контейнеры наружу высовывают .begin(), .cbegin() — то есть тег константности уже пошёл в имя метода, что не есть гуд. Напоминает венгерскую нотацию, кстати.

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

Да тут половину чата может создать собственный ЯП, это не rocket science.

Уровня C++, Rust или даже Go?

Создавать ЯП уровня (монструозности?) C++, Rust или Go не нужно — эти языки уже существуют.

Я согласен с byko3y в том смысле, что написать транслятор своего языка во <что-то стандартное> — путь в общем-то многократно пройденный и (подозреваю) подробно описанный. Особенно для всяких брейнфаков 🙂

Чтобы язык получил доступ к железу, <что-то стандартное> = LLVM IR, например.

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

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

vector<int> v;
auto const & cv{v};

auto const_cv_it = cv.begin();  // всё просто
auto const_v_it = const_cast<decltype(v) const &>(v).begin();  // непросто
auto const_v_it2 = v.cbegin();  // опять просто
rupert ★★★★★
()
Ответ на: комментарий от Crocodoom

Создавать ЯП уровня (монструозности?) C++, Rust или Go не нужно — эти языки уже существуют.

На момент появления Rust и Go была точно такая же ситуация: уже была куча языков разной степени продвинутости, распространенности и востребованости.

Тем не менее, у них всех был какой-то фатальный недостаток и Rust с Go появились. Как и Dart, например. Или Kotlin.

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

И вот на рождение такой идеи не способен ни я, ни buko3y, ни 99% посетителей LOR-а.

eao197 ★★★★★
()
Закрыто добавление комментариев для недавно зарегистрированных пользователей (со score < 50)