Pull to refresh

Comments 216

Еще было бы интересно non-virtual abstract, т .е.

void foo() = 0; // без virtual

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

это решается концептом в месте применения, по аналогии с указанием ABC в виртуальном случае

Именно что в месте применения.

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

Можно обмазывать проверками - SFINAE, requires - шаблоны функций-членов. Потому что шаблоны будут инстанцированы уже после того, как финальный класс определён.

Если не CRTP, то писать void func(const DerivedFromBase auto& instance); выглядит вполне хорошо. Ничем не хуже олдскульного виртуального void func(const AbstractBase& instance). И при ошибке современные компиляторы в ошибке разжуют, почему именно концепт не применим.

Если CRTP, то да, надо закапывать в тело члена (в конструктор - мимо него не пройдешь, если это не класс-как-пространство имен чисто из статических членов). Не прям вот идеально, не template<DerivedFrombase T> , но вполне себе чистенько. Благо, с современными компиляторы static_assert(DerivedFromBase<T>); тоже будет по полочкам разложен в ошибке.

Дык концепт + crtp

Не получится.

Если в CRTP-базе сделать requires или static_assert на уровне класса, то при попытке унаследовать произойдёт довольно подлая штука. А именно, CRTP-шаблон параметризуется неполным типом! Вот так вот.

template<class T>
  requires some_constrain<T>
struct base {
  static_assert(something_about<T>);
  .....
};

struct derived // в этом месте он ещё неполный
  : base<derived>
  // some_constrain<T> получил неполный тип
  // something_about<T> получил неполный тип
{
  .....
}; // и вот только здесь он стал полным

В методе после полного определения можно будет уже проверить с помощью require и static assert, не? Редко пишу на шаблонах, не оч понимаю какая разница где проверять.

void foo() = delete;

Вводит и запрещает эту сигнатуру в этом скопе. В скопе наследников (или в другом неймспейсе) можно перегрузить и использовать оттуда.

Но это не будет требованием. Я не смогу писать Iface->foo, потому что нет гарантий, что у класса под интерфейсом этот foo есть. А если каждый раз нужно будет явно указывать derived, то начинает пропадать смысл интерфейса.

Если обращаешься через интерфейс, то каким волшебным образом там будет доступна наследничья реализация невиртуальной функции?

А для шаблонов - ну, концепты и констрейны в помощь. Хотя и с оговорками. Как я уже написал ранее, CRTP не дружит с констрейнами. https://godbolt.org/z/dnhqz51qb

Как я уже написал ранее, CRTP не дружит с констрейнами.

Дружит, если через соседский огород ходить: https://godbolt.org/z/EczfKTY54.

Но у этого есть очевидная проблема.

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

блин, еще забыл про final и пачку проблем с ним

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

Предвидели видимо

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

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

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

Про то, что inline для функции и для переменной противоположны - неправда.

И там и там в объектных файлах создаются объекты (в секции кода и в секции данных, соответственно), помеченные для линкера как "оставь единственный экземпляр". У MSVC для этого был атрибут __declspec(selectany).

При этом все указатели на инлайн-функцию и инлайн-переменную принимают одинаковое значение. Это важно, например, для параметров шаблонов.

И все статические переменные внутри тела функции также "оставь единственный экземпляр".

Собственно, это всё меры для реализации ODR.

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

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

Вообще, если подумать при проектировании грамматики языка, то ключевые слова могут не конфликтовать с именами идентификаторов. Например, в Bash вполне валидная конструкция export export=export. Она присваивает переменной окружения по имени export значение export и все живы.

export export=export

Вот такого точно не надо, код станет еще запутаннее. И bash это тоже антипример.

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

И bash это тоже антипример.

Ну ок, JS/TS: const obj = { if: 1 }

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

Реализация в PHP прекрасна: идентификатор переменной должен начинаться с $. Поэтому переменные в любых контекстах всегда различимы.

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

но это везде порицаемо, спецсимволы не должны пролезать в имена, с $ видимо недоработка вышла

Нельзя так-то, но компиляторы разрешают как расширение. Есть https://wg21.link/P4234 предложение, чтобы стало можно там, где это можно.

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

А вы уверены, что тут нужно править именно лексер? Мне кажется, что подобная логика должна анализироваться даже не в парсере (так как с точки зрения синтаксиса тут все корректно), а на еще на уровень выше, в анализаторе.

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

Ниже-выше зависит от направления взгляда.

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

поэтому там в принципе не может быть зависимости от контекста

В контекстно-независимом языке из "export export=export" лексер выплёвывает что-то типа {keyword, space, keyword, sign_eq, keyword}, потом парсер обламывает.

В контекстно-зависимом языке [где можно так писать] лексер должен выплюнуть {keyword, space, identifier, sign_eq, identifier} чтобы парсер не обломал. Вот для того чтобы понимать почему "export" то keyword, то identifier и требуется понимание контекста.

Вы, конечно, можете возразить, мол, пусть гонит первый вариант, а уже парсер пусть разбирает, что вот тут keyword это keyword, а вон там — identifier. Но тогда получается, что мы на парсер перекладываем работу лексера [по определению типа токена]. Жабогадюкинг.

Но тогда получается, что мы на парсер перекладываем работу лексера [по определению типа токена].

Так это и есть контекстно-зависиый синтаксис, о котором лексер на его конечных автоматах ничего знать не должен :-)

Для меня ситуации "лексер знает о работе уровня парсера" и "парсер знает о работе уровня лексера" одинаково "ниочень".

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

мы на парсер перекладываем работу лексера

И чем это плохо?

Нифига. У C и С++ синтаксис контекстно-зависимый, к сожалению.

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

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

Парсер - да, но не лексер.

Парсер - да, а не только анализатор.

А вы уверены, что тут нужно править именно лексер? Мне кажется, что подобная логика должна анализироваться даже не в парсере (так как с точки зрения синтаксиса тут все корректно), а на еще на уровень выше, в анализаторе.

Согласен. Иначе парсер не сможет сделать правильный узел АСТ.

Внезапно, - но лексеру пофиг.

Лексер гонит поток лексем "закорючка такая, закорючка этакая, слово латиницей".

А вот уже парсер смотрит, является ли это слово служебным или рядовым. Или как следует трактовать ту или иную закорючку. >> это один сдвиг или две угловые скобки? А!

https://github.com/llvm/llvm-project/blob/main/clang/lib/Lex/Lexer.cpp
https://github.com/llvm/llvm-project/blob/main/clang/include/clang/Basic/TokenKinds.def#L372


лексер маппит напрямую "class" -> kw_class, превращая текст в поток токенов, так что

> парсер смотрит, является ли это слово служебным или рядовым

Не соответствует коду


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

Не нужно.

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

Идеальный язык для синдрома самозванца

Как разработчик компиляторов я пишу на разных диалектах C++:

Когда меня приглашают на собеседования как знатока C++, я обычно отказываюсь. Надоело объяснять, что мой С++ особенный.

В OpenJDK Hotspot C++ ограничен по максимуму, отладка и стабильность во главе всего (попробуйте отладить JVM где JIT генерирует и перегенерирует код и куча потоков):

Features from the C++98/03 language may be used unless explicitly forbidden here. Features from C++11, C++14, and C++17 may be explicitly permitted or explicitly forbidden, and discussed accordingly here. There is a third category, undecided features, about which HotSpot developers have not yet reached a consensus, or perhaps have not discussed at all. Use of these features is also forbidden. … Historically, HotSpot has mostly avoided use of the Standard Library.

(It used to be impossible to use most of it in shared code, because the build configuration for Solaris with Solaris Studio made all but a couple of pieces inaccessible. Support for header-only parts was added in mid-2017. Support for Solaris was removed in 2020.)

LLVM более демократичный:

Unless otherwise documented, LLVM subprojects are written using standard C++17 code and avoid unnecessary vendor-specific extensions. … Instead of implementing custom data structures, we encourage the use of C++ standard library facilities or LLVM support libraries whenever they are available for a particular task. LLVM and related projects emphasize and rely on the standard library facilities and the LLVM support libraries as much as possible. … When both C++ and the LLVM support libraries provide similar functionality, and there isn’t a specific reason to favor the C++ implementation, it is generally preferable to use the LLVM library. For example, llvm::DenseMap should almost always be used instead of std::map or std::unordered_map, and llvm::SmallVector should usually be used instead of std::vector.

We explicitly avoid some standard facilities, like the I/O streams, and instead use LLVM’s streams library.

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

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

> Когда меня приглашают на собеседования как знатока C++, я обычно отказываюсь. Надоело объяснять, что мой С++ особенный.

моё личное мнение (как контрибьютора LLVM) - все знающие хорошо С++ знают его одинаково, с одинаковым набором фич. "диалектами" можно назвать QT и unreal engine, где есть свои кастомные расширения, но и там основа есть та же. И есть пара стилей

  • С-стайл - неоправданно близко к С

  • "слишком модерн С++" - шаблоны ради ничего, переусложнения, накрученные ranges

  • "поганая джава" - бесконечные интерфейсы и менеджеры

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

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

Те, кто осознал C++, могут писать и на фортране, и на хаскелле, но стараются щадить своих коллег.

А лисп всё равно самозародится, как мыши в сене. Это неизбежно, смиримся.

На любом тьюринг-полном языке можно писать на хаскелле.

foo :: Eq a => a -> a -> Bool

Вот тут я чётко вижу, что функция позволяет делать x == y и x /= y. Я чётко вижу, что функция НЕ позволяет сделать, например, rm -rf.

template<typename T>
bool foo(T x, T y);

Вот тут я чётко вижу, что функция позволяет сделать что угодно, что в принципе возможно сделать из C++.

Покажите, пожалуйста, как на C++ можно писать на Хаскелле хотя бы в той мере, чтобы foo из C++ стала такой же как foo из Хаскелля.

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

#include <concepts>

// или руками напишем
template<class T> concept ad_hoc_eq = requires (T& x, T& y) { (bool)(x == y); };

template<std::equality_comparable T> auto foo(T, T) -> bool;

// вариант с GADT
// (когда мы не требуем, чтобы оба аргумента были одного типа)
// (и вообще не задумываемся про их тип)
auto foo(
  std::equality_comparable auto,
  std::equality_comparable auto
) -> bool;

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

Я думаю, это не то, что хотел увидеть вопрошающий. foo по прежнему может дернуть любой публичный член своих аргументов (звучит как-то... nsfw). Или может дернуть `system("rm -rf /");`. Если про дерганье членов я отписал рядышком, то вот увидеть по определению функции, что она точно не дергает ничего типа system в C++ действительно нельзя.

template<typename T> struct Eq
{
    bool operator==(const T& other) = 0;
    bool operator!=(const T& other) {return !(*this == other);}
};

template<typename T> bool foo(const Eq<T>& x, const Eq<T>& y);

Как-то примерно так.

Что мешает мне внутри foo вызывать, скажем, print("fuck you")? Прочитайте ещё раз первый абзац прошлого комментария.

Ничто. Pure функции в C++ не завезли, увы. Впрочем, это не проблема C++, и он тут не одинок. Впрочем, вы не критиковали C++ per se, а отвечали на комментарий "на любом языке можно писать на хаскеле". Впрочем, этот комментарий имел в виду, что на нем можно писать в функциональном стиле, а не что можно из C++ сделать хаскель со всеми его гарантиями. Поэтому в foo не будет print не потому, что С++ это запретит, а потому, что вы его туда не напишете.

Поэтому в foo не будет print не потому, что С++ это запретит, а потому, что вы его туда не напишете.

Всё было бы хорошо, если не было плохо. Я-то сам, конечно, не буду себе в ноги стрелять (насколько могу, т.е.), но вот когда такая функция торчит из либы, то с гарантиями как-то интереснее.

А если всё-таки упороться, - то... ну, придётся затащить словари. Только это уже будет явно.

using Object = std::shared_ptr<void>; // раз уж начали городить рантайм

struct IEqTraits { virtual bool op_eq(Object, Object) const = 0; };

template<class T> struct EqTraits : IEqTraits {
  bool op_eq(Object a, Object b) const override {
    auto ta = std::static_pointer_cast<T>(a);
    auto tb = std::static_pointer_cast<T>(b);
    return *ta == *tb;
  }
};

bool foo(
  // словари идут впереди аргументов
  IEqTraits const* eq,
  // упороться каррингом - это отдельное развлечение (не сейчас)
  Object a, Object b)
{
  return eq->op_eq(a, b) && eq->op_eq(b, a);
}

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

Как раз никокой идиотии нет. Исходникам JVM Hotspot порядка 25 лет. Там полно легаси кода. Просто так код никто не трогает. Если бы старались использовать все новые фичи C++, то было бы такое наслоение стилей. Никому не хочется заниматься отладкой C++. Без этого куча проблем с отладкой, особенно GC кода. Разработчики хотят работать на решением задач JVM, а не C++.

моё личное мнение (как контрибьютора LLVM) - все знающие хорошо С++ знают его одинаково

Перефразирую Сократа :) “я знаю, что ничего не знаю в C++” Мое знание С++ ограничено тем, что я использую часто. То что, не используется, очень быстро забывается.

Этот список ходит по инторнетам лет 25, примерно со второй книги Майерса.

Где-то по дороге потеряли 15 разных значений f(x).

За это время вышло штук 7 мажорных апдейтов языка, но в целом стало только хуже, потому что deprecated не завезли, и а вдруг вы захотите скомпилить код из 1969 года.

Если auto_ptr стал deprecated в С++11, то должен был быть полностью выпилен в 14 или максимум в 17. Иначе это просто ни к чему не обязывающие пожелания каких-то потусторонних дядей.

Забавно, что даже в С выпиливание устаревших фич есть (напр., триграммы, синтаксис K&R). Но не в плюсах...

Ну так в стандарте языка С++17 auto_ptr был выпилен -- вон для кого выше написано "removed"? В списке классов стандартной библиотеки начиная с С++17 он отсутствует.

То, что GCC и clang только лишь выдают warning -- это самодеятельность компиляторов, они и кучу других отсутствущих в стандарте вещей предоставляют. MSVC код с использованием auto_ptr начиная с C++17 не компилирует.

Презабавная статья!

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

  • процедурное

  • объектно-ориентированное

  • шаблонное (generic programmimg)

  • функциональное

  • параллельное (и, что очень важно, с четко определенной семантикой модели памяти)

Язык предлагает несколько уровней абстракций. Не буду их анализировать здесь. Это отдельная тема.

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

Существует огромное количество библиотек и фреймворков.

Сотни тысяч (миллионы) программистов знают (хотя и в разной степени) этот язык.

Язык хорошо стандартизирован (есть комитет по стандартизации, в котором представлены интересы больших корпораций) и документирован.

В совокупности, все эти факторы позволяет использовать язык для разработки практически любых классов приложений: моделирование, обработка данных, системы управления, системы реального времени, встроенные приложения (микроконтроллеры, GPU), системы высокочастотной торговли, сервисы, сетевые приложения, базы данных, игры, 99% модулей для Пайтона, и т.д. и т.п.

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

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

std::random_device rd;
std::mt19937 gen(rd());                          // а что такое mt19937?
std::uniform_int_distribution<int> dist(1, 100); // а почему отдельно?
int value = dist(gen);                           // ну наконец-то

Ответ здесь очень простой. Когда речь идет о генерации случайных чисел, то есть две вещи:

  • функция распределения (uniform, Gauss, etc.). Строка 3 вверху.

  • собственно генератор случайных (или псевдо-случайных) последовательностей. Строка 2 вверху.

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

  • дефолтный алгоритм mt19937 генерирует псевдослучайную последовательность при умеренной производительности. Это делает его приемлемым для большинства приложений, где не требуется ничего иного.

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

  • либо может потребоваться очень быстрый генератор (заметьте, что mt19937 является довольно медленным). И тогда программист может разработать свою версию которая удовлетворяет стандартному интерфейсу.

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

Ну и если уж речь идет о том, что Вам непонятно что такое mt19937то напишите простой класс и используйте его в своем коде так, как если бы Вы писали на Пайтоне или Ржавчине :)

claсс RandomUniformInt {
public:
    MyRandomInt(int minVal, maxVal) : _gen(_rd()), _dist(minVal, maxVal) {}
    int next() { return _dist(_gen);}
private:
    std::random_device _rd;
    std::mt19937 _gen;
    std::uniform_int_distribution<int> _dist;
};

// Client code

RandomUniformInt randInt(1, 100);
int val = randInt.next();

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

Зато автор пишет программы и понимает что нужно обычному программисту.

Не нужны ему все эти детали реализации в 99.99 процентах программ. Ему нужно случайное число от 0 до 100. И в удобных языках есть метод random(max_value) который дает именно то что нужно программисту в подавляющем большинстве случаев.

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

"Oбычные" программисты (т.е. "джуны"), которые даже не понимают что стоит за словом "rand" скоро никому не будут нужны. Их сейчас увольняют пачками, успешно заменяя ИИ. Реально выживут (или проживут дольше) лишь инженеры с хорошим математическим образованием и архитектурным видением.

Библиотеки это, понятное дело, хорошо. Однако, для получения качественного продукта, необходимо хорошо понимать характеристики (в плане функциональности, масштабируемости и производительности) используемых библиотек. Вот даже автор статьи, похоже, понял разницу между std::map и std::unordered_map.

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

Эта разница как бы не в мемах уже объясняется.

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

Если есть четкое понимание, что и зачем делается, что мешает просто использовать rand? Да, устаревшее, но в языке-то осталось.

В него правую границу передать нельзя. Наивные реализации правой границы работают неверно. Эта функция тоже не для людей сделана.

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

Эта проблема тоже давно решена. Сид должен быть необязательным параметром для инициализации.

Если не указали - вам дадут что-то случайное. И это хороший дефолт.

Если указали - будет вам стабильная последовательность. Для тестов полезно.

Обычному сеньору […] все равно нужно только случайное число от 0 до 100

Этот же самый “обычный сеньор” будет на каждом углу доказывать, что синглтоны и скрытый глобальный стейт — это плохо и антипаттерн. Так вот, функция, возвращающая “просто случайное число” — это и есть скрытый глобальный (скорее, тред-локальный) стейт.

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

int a = std:dummy_random_in_range<int>(-5, 5); // Случайное число от -5 то 5 включительно
double f = std::dummy_random<float>(1); // случайное в интервале [0,1) (не включая 1)

Но уже тут возникают вопросы: а у этого random seed будет константый (т.е. повторяемый между запусками) или случайный? И для float возможно нужны несколько реализаций, включающих и не включающих граничные значения. Видимо потому и не включили, что не смогли договориться, как надо просто.

Ему нужно случайное число от 0 до 100

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

  • Генерируете ключ шифрования, IV или токен — вам нужен максимально настоящий и качественный рандом, с тем или иным источником энтропии из реального мира.

  • Генерируете входные воздействия для тестов или решаете что-то методом Монте-Карло — вам нужен генератор псевдослучайных чисел с определённым балансом между качеством и производительностью.

в удобных языках есть метод random(max_value)

Который, как правило, не подходит для серьёзных применений.

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

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

Генерируете входные воздействия для тестов или решаете что-то методом Монте-Карло — вам нужен генератор псевдослучайных чисел с определённым балансом между качеством и производительностью.

Просто задайте сид. Один параметр и только там где он нужен.

Качество по дефолту подойдет в тех же 99.99 процентах случаев.

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

Я слишком туп чтобы писать криптографию.

Это плохой класс, при таком использовании слишком часто будет создаваться новый mt19937, что убивает всю идею mt19937. Если уж так делать - проще сразу _dist(_rd) писать в next()

А если мы всё-таки используем std::mt19937, то надо делать как-то так (пишу по документации, код не проверял):

claсс Random {
private:
    std::mt19937 _gen;
public:
    Random() : _gen(std::random_device()()) { }

    int next(int minVal, maxVal) {
      std::uniform_int_distribution<int> dist(minVal, maxVal);
      return dist(_gen);
    }

    static Random& shared() {
      thread_local Random instance;
      return &instance;
    }
};

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

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

Аминь, золотые слова. Плюсанул бы, да крив на лицо по стандартам хабра.

В стандартной библиотеке Rust вообще нет функции рандома и никто не требует.

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

Читаю и недоумеваю. Десятилетиями пишу на C++, с 98 по 23, никаких особых проблем не было никогда. У меня какой-то другой C++?

Не. У меня дед всю жизнь проездил на Запорожце, был полностью доволен, любил его даже: раз в неделю в нём что-то смазывал. /s

Ну уж если с авто сравнивать, C++ - это, скорее, Феррари. Водить надо уметь, но гоняет - другим не чета.

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

Оптимизатор у хорошего C++ компайлера даст 100 очков вперед другим языкам. Перформанс, по крайней мере, в моей области (электронные CAD-ы), важен примерно всегда, и почти всегда - узкое место.

Но не всем. Watcom C Compiler обойдет на 10 очков вперед. )))

Существует пласт системного ПО, созданного исключительно на ассемблере:

  • KolibriOS (и MenuetOS) - полноценные и современные операционные системы, которые включая ядро, драйверы, текстовые и графические редакторы, игры и браузер, написаны на ассемблере (FASM). При этом KolibriOS помещается на одну дискету, загружается за пару секунд и мгновенно работает даже на древних ПК.

  • FASM (Flat Assembler) - популярный современный компилятор ассемблера, который сам полностью написан на ассемблере. Он самостоятельно компилирует свой собственный код.

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

  • OpenSSL / BoringSSL - криптографические библиотеки, обеспечивающие безопасность почти всего современного интернета (HTTPS). Самые ресурсоемкие алгоритмы шифрования (AES, RSA, ChaCha20) написаны на чистом ассемблере x86-64 и ARM для задействования аппаратного ускорения процессоров.

  • WireGuard - современный и быстрый протокол VPN. Его криптографическая база во многом опирается на оптимизированный вручную ассемблерный код.

Обработка видео и аудио в реальном времени требует колоссальной вычислительной мощности:

  • FFmpeg / x264 / x265 - движки, на которых работает практически всё современное цифровое видео (включая плееры VLC, YouTube, стриминги). Сверхбыстрое сжатие и декодирование видеопотоков обеспечивается за счет сотен тысяч строк кода, написанных на ассемблере под SIMD-инструкции (AVX-512, SSE, NEON).

  • FLAC / WebM - аудио и видеокодеки содержат низкоуровневые ассемблерные оптимизации для минимизации задержек при воспроизведении.

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

У ассамблера есть маленький недостаток - он камнезависимый.

Поэтому получится, что тот же ffmpeg на Intel и ffmpeg на Arm64 должен использовать разные коды, а в некоторых случаях и передавать выполнение встроенным в камень же аппаратным x264-декодерам.

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

Ладно там ARM. Код на P и E ядрах одного и того же Intel процессора должен использовать разный ассемблер. Иначе P ядра не выйдут на всю силу. Что и было частью провала этой архитектуры: Intel компилятор так умеет, а остальные оси и компилируемые языки не очень.

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

Линус Торвальдс много раз объяснял почему Linux на C и в нём никогда не будет C++. И одна из причин было то, что он не хочет, чтобы код в его проекте писали C++ программисты. При этом Rust был им допущен и уже есть в исходниках Linux.

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

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

А он и не помрёт, ибо дофига легаси. И даже новые проекты активно появляются. Плюс весь геймдев.

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

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

Ну КОБОЛ тоже жив в каком-то смысле

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

в котором нету нормального наследования

А оно точно надо? Вот прям никуда без него?

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

Иногда надо, иногда нет.

А тут выбор уже сделали за вас.

Неистово плюсую, всё верно. И еще вот это всё привело к тому что язык стал... неэстетичным, что-ли. Вот к примеру std::move - почему такая громоздкая конструкция, если по смыслу это вообще должен быть унарный оператор? А есть еще std::forward, std::move_if_noexcept, std::decay, std::launder, std::common_type, std::remove_reference, std::remove_const, std::enable_if, std::conditional и прочие is_trivially_default_constructible. Особенно если все это навёрнуто в трехэтажных шаблонах.

Всё это выглядит как огромное количество "кишок наружу" - какие-то шаблонные конструции, которые вроде как и нужны (наверное, мне ни разу не понадобились), но... в них не хочется углубляться. На языке хочется писать прикладные программы, а не заниматься странными хаками компилятора.

В качестве контрпримера могу привести библиотеку Qt - огромная библиотека, но всё строго и по делу. Приятный синтаксис, богатые классы, решающие реальные прикладные задачи. Шаблоны используются строго для того, для чего они и задумывались - параметризация контейнеров. Даже camelCase выглядит приятнее чем snake_case.

Я что-то путаю или можно после заголовочных файлов написать using namespace std; и потом не использовать std:: перед каждым чихом?

Можно, но тогда в текущем пространстве имён внезапно могут появиться move, data, begin, end, size, span, list, map, queue, erase и прочие однословные классы и функции.

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

Можно но не нужно.

Можно и нужно писать using std::achoo;

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

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

А матрица - частный случай тензора:)

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

Спасибо за статью. Хоть в основном все вышеперечисленное знаю, почитать было интересно. C++ действительно не самый быстрый но самый совместимый. Но он настолько разнообразен, что на нем можно писать так, чтобы было быстро.

Но я бы упомянул еще несколько моментов:

  1. В вариантах целочисленных типов не упомянуты size_t без std и intptr_t.

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

  3. Самая большая боль C++ это undefined behavior. И дело даже не в разименовании неинициализированного или деаллоцированного указателя, а в том как компиляторы по факту трактуют стандарт. Если в стандарте написанно что XXX может производить - это UB, а разработчики стандарта считают что программист пишет без UB и делают соответствующие предположения о коде. Например известная история с переполнением Int.

  4. Есть еще одна проблема c и c++, это strict aliasing rules. Дополнительные правила, которые усложняют каст указателя на один тип к другому чтобы поковыряться в битах.

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

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

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

В стандарте C++ четко сказано: "Переменная вводится в программу объявлением (declaration) и имеет идентификатор (имя)".

это когда же у нас вызов функции и передача аргумента вдруг стало причиной инициализации переменной?

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

struct S {
int x = 5;
int y{10};
S() : y{2}, x(1) {}
};

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

return x;
return {1, 2, 3};
throw x;

И это вообще и близко, тоже не переменные.

"Простые вещи просто не делаются"

Ну оберни в функцию и будет тебе всё по одной кнопке? За чем своё субъективное ощущение после условного питона(или джавы), выдавать за некую справедливую оценку языка?

"Проблемы кастинга"

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

Короче дальше мне стало нудно читать ибо:

1. Автор выдаёт субъективное ощущение за объективное оценивание

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

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

А на стеке она, в регистре или вовсе оптимизирована в ноль - нет разницы.

Кроме того, в математике параметры функций открыто называют переменными, и само слово “переменная” именно из математики и пришло.

ну да, перефразируя известное высказывание:

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

можно сказать

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

С++ это язык именно программирования, именно для программистов, а не для физиков, лириков, ...

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

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

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

via https://habr.com/ru/articles/1047890/comments/#comment_30177360

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

Ну как минимум потому что Rust выглядит как очередная кривая обёртка над С++.

Вот две функции для записи в память другого процесса:

RUST:

fn write_memory(handle: HANDLE, address: usize, data: &[u8]) -> Result<usize, Error> {
    let mut bytes_written = 0usize;
    unsafe {
        WriteProcessMemory(
            handle,
            address as *mut _,
            data.as_ptr() as *const _,
            data.len(),
            Some(&mut bytes_written),
        )?;
    }
    Ok(bytes_written)
}

C++:

SIZE_T WriteProcessMemoryEx(HANDLE hProcess, LPVOID address, LPCVOID buffer, SIZE_T size) {
    SIZE_T bytesWritten = 0;
   WriteProcessMemory(hProcess, address, buffer, size, &bytesWritten);
     return bytesWritten;
}

Вам нужно объяснять? Или вы проведёте сравнение и увидите простоту и лёгкость С++ в базовой задаче?

Вы действительно считаете что RUST выглядит и есть лучше чем C++ ?



P.s. На меня накинулись фанаты Rust, у которых от их языка уже проблемы с логикой, так как они не понимают что тут слова про память просто пример, а не аргумент

Вопрос в том, что считать “базовой задачей”. Для тех, кто предпочитает Rust, описанное вами - не базовая задача, а редкий случай, который надо сделать один раз, протестировать как следует и больше на него не смотреть. Для тех, кто предпочитает C++ - вероятно, наоборот.

Так в смысле, обёртка над функцией не базовая задача? Одно из самых частых это обёртка над функцией с некоторой логикой.

Я показал пример обёртки над функцией, можно даже не знать что она делает(функция).

Смысл в том, что в С++ всё красиво и аккуратно, не каких лишних букв и символ.

Вот зачем отделять тип и словесный ид двоеточием, что это за порнография?

-> Result<usize, Error>


А вот что это за порнография ??

обёртка над функцией не базовая задача?

Над функцией, которая лезет напрямую в WinAPI (и вообще в сторонний C API)? Не для всех уж точно.

Вот зачем отделять тип и словесный ид двоеточием, что это за порнография?

А. То есть вопрос на самом деле вообще не в “кривой обёрточности”, а просто в том, что синтаксис некрасивый?

Одно из самых частых это обёртка над функцией с некоторой логикой.

Только обычно логики 90%, а бойлерплейта 10% (условно), в вашем примере 0% и 100% соответственно.

в С++ всё красиво и аккуратно, не каких лишних букв и символ

Вы тут де-факто на Си пишете. Почему указатель + длина, а не std::span?

Вот зачем отделять тип и словесный ид двоеточием, что это за порнография?

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

https://habr.com/ru/articles/532660/comments/#comment_22487588 (также https://habr.com/ru/articles/532660/comments/#comment_22487564, https://habr.com/ru/articles/532660/comments/#comment_22488240)

А вот что это за порнография ??

Это std::expected<std::size_t, your_error_type>.

Пережиток паскаля и делфи

Удачное решение Вирта.

Действительно, ведь 95% целевой аудитории занимаются только тем, что пишут в память других процессов на Win32.

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

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

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

ДА ПРИ ЧЁМ ТУТ ПАМЯТЬ, ЧТО ВЫ К НЕЙ ПРИСТАЛИ, ЭТО ПРОСТО КАК ПРИМЕР.

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

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

Я такого не говорил ("Вот сразу видно любителей C++ и т.п. У них проблемы с логикой"), я писал, что вызовы из одного языка программирования функций из другого языка программирования обречены иметь бойлерплейт и прочий некрасивый код. Вы же из одного такого примера пытаетесь вывести, что весь Rust плохой, а C++ хороший.

ДА ПРИ ЧЁМ ТУТ ПАМЯТЬ, ЧТО ВЫ К НЕЙ ПРИСТАЛИ, ЭТО ПРОСТО КАК ПРИМЕР.

Успокойтесь, к памяти никто не приставал, я даже этого слова не писал. Вместо WriteProcessMemory могло быть большинство других сишных функций, и все аргументы остались бы действительны.

У них проблемы с логикой

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

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

Гениально.

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

"Во-вторых, ясное дело, что если вы будете вызывать из языка"

что вызывать то?

"где принято использовать другие, не Си-подобные приёмы"

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

"тонкую обёртку над сишной функцией, у вас будет некрасивый обвязочный код"

Ну да, конфета плохая не из-за производства, а из-за фантика, я вас понял.

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

Некрасивый обвязочный код, сугубо из-за того что сам язык такой

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

Гениально.

(Выделение моё) Не "точно" -- из "обёртка -- криво" не следует "сложная логика -- хорошо", но! из "обёртка -- криво" не следует "сложная логика -- криво". Если вы хотите сравнить качество двух языков, то сравнивайте их на наборе примеров и использований, которые соответствуют их применениям в реальности.

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

"Во-вторых, ясное дело, что если вы будете вызывать из языка"

что вызывать то?

(Выделение моё)

"где принято использовать другие, не Си-подобные приёмы"

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

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

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

но нет надо ведь всё испортить

Это вы про C++?~

"тонкую обёртку над сишной функцией, у вас будет некрасивый обвязочный код"

Ну да, конфета плохая не из-за производства, а из-за фантика, я вас понял.

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

Некрасивый обвязочный код, сугубо из-за того что сам язык такой

Что было: в коде на Rust функции из C++ вызываются некрасиво;
Ваше обобщение: в коде на языке X функции из языка Y вызываются некрасиво => X -- плохой язык.

Проведите мысленный эксперимент, в котором вы из C/C++/любого другого языка вызываете код на Rust/Python/Java/любого другого языка, и поймите, почему такой подход неверен.

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

Кстати единственно что не нравится это отсутствие типа данных для HEX чисел, приходится через указатель, но в RUST его тоже нет.

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

Вот про это были Си, и теперь же С++, упростить это нельзя.

И вот отсюда и простая реализация, указываешь есть ли данные на выходе, название функции и перечисление аргументов функции их тип и название. ВСЁ. Что вы туда запихнуть пытаетесь? Если ничего, так зачем порождать миллион языков, которые что то там пытаются?

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

Это, может быть, про Си, но не про C++:

decltype(auto) foo() {
   int i = 1;
   return (i); // UB
   // return i; // Нет UB
}

Обычно на все трудности С++ ответом является: программист должен это знать/никогда так не делайте и т.п., но эти аргументы верны для любого языка программирования вообще, так как представляют себе "бесконечно опытного" программиста и игнорируют необходимость обучения и то, что дизайн языка бывает более или менее качественным.

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

Ответственно заявляю, что в Rust именно такой подход и принят, и поддерживается он сильно в большей степени, чем в C++, так как в Rust изначально были Result и Option, синтаксический сахар, pattern matching и прочее, до чего C/C++ с errno и его аналогами и out-параметрами как до Луны, а std::optional и std::expected поддерживаются слабо.

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

?

<...> ВСЁ. Что вы туда запихнуть пытаетесь? Если ничего, так зачем порождать миллион языков, которые что то там пытаются?

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

decltype(auto) foo() {
int i = 1;
return (i);
}

Вы где такое увидели вообще?

Вот и аргумент "никогда так не делайте". Этот фрагмент кода опровергает 'примитивно, означает что всё прозрачно и понятно, "Если вам нужно сделать шаг вперёд, вы просто его делаете, вам не нужно делать кульбит", С++ как раз про это'; также показывает неочевидность синтаксиса -- кто подумает, что (x) и x это семантически разные вещи.

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

Все всегда пишут:

int foo() {
int i = 1;
return 1;
}

И так всегда и во всём, в том числе в коде от майкрософт.

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

где вы нашли этот кусок кода

Он искуственный, да. https://habr.com/ru/articles/350186/comments/#comment_10693598

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

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

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

Если вы или ещё кто-то, психически болен, и начинает усложнять конструкты, то кто вам виноват?

Попробуйте на языке ПРОГРАММИРОВАТЬ, а не искать извращённые способы чтобы потом кидаться ними в комментариях.

А единственный явный плюс у RUST, который выделяют это Borrow Checker . Ну то есть снова кто-то не осилил указатели и пошёл писать очередной сборщик мусора, но продвинутый.

То есть вся слава языка просто из-за этого.

Ну то есть были те, кто не смогли запомнить простое правило, написал new, напиши delete , или calloc/malloc, напиши free

А теперь самое интересное, внешние библиотеки ваша фича не проверяет, интересно сколько времени уйдёт у майкрософт на переписывание DirectX с плюсов на RUST ?

А единственный явный плюс у RUST, который выделяют это Borrow Checker . Ну то есть снова кто-то не осилил указатели и пошёл писать очередной сборщик мусора, но продвинутый.

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

А RAII в Расте есть, конечно, куда ж без него, только прямого отношения к Borrow Checker это не имеет. RAII и в C++ есть...

Ну надо ведь юмор уметь понимать.

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

Получается сборщик мусора, и да, раз вы такой грамотный то RAII не сборщик мусора, это идея что бы в конструктор выделение памяти, а в деконструктор её освобождение

BC это как раз и есть вариация RAII

В обобщенном коде и обычный auto, и decltype(auto) встретить не проблема (тыц или тыц).

Такое может получиться при разворачивании макроса (которые по известным причинам свои параметры в скобки оборачивают) который из-за ifdef'ов и/или других макрсов, которые он использует, в одном случае раскрывается в какое-то выражение, а в другом просто в (arg).

На Java, C# и Python обёртки вокруг C-шных функций из Win32 API будут выглядеть примерно так же. Красота самих обёрточных функций никого не волнует. Зато Rust-функция принимает на один параметр меньше.

На один параметр меньше, потому что функциональность неодинаковая.

Мне война языков по барабану, но пример явно неудачный. При взгляде на код на Расте я сразу вижу, что если записать в память не удалось, наверх побежала ошибка, которую нельзя не обработать. А в коде на С++ я вообще не вижу, что происходит в случае ошибки. Гугл говорит, что WriteProcessMemory возвращает BOOL, ну и т.д.

У WinAPI своя логика, подробная ошибка, вот что бы прям очень подробно, получаем через GetLastError, а так возвращение BOOL указывает на то успешно завершилась функция или нет.

Если не успешно, то дёргаем GetLastError , но это вообще не С++, а библиотека на нём

Код на С++ не проверяет возвращаемое значение. Откуда станет известно, что произошла ошибка?

про Rust не могу ничего сказать, ни разу не приходилось сталкиваться с каким-то кодом на Rust. Обрывки видел какие-то 5-10 строк, но ни разу не видел какого-то законченного алгоритма или решения задачи предметной области. Может это мне так не повезло, а может это отражение какой-то глобальной статистики на меня, я не знаю! В любом случае мое мнение совершенно субъективно, прошу воспринимать его именно так.

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

Вывод: если вы не имеете оснований считать, что исследовали тему, не высказывайте своё мнение так резко. /thread

а в чем резкость-то? В том что я маленько историю знаю и могу проводить аналогии?

Если взялись советовать - советуйте до конца, иначе какой смысл в ваших советах?

аналогии

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

Сказав "С++ это язык именно программирования, именно для программистов, а не для физиков, лириков. Программисты должны понимать откуда взялся, что значит, и почему он именно static, например" (выделение моё) вы проводите соответствие: пишет на C++ -- программист, не на C++ -- не программист. При том, что второе может быть верно, и языки "для физиков" тоже есть, вы утверждаете, что нет языков программирования, при работе с которыми "программисты должны понимать откуда взялся, что значит, и почему он именно static, например", и забываете тех, кто знает теорию, но может на C++ и не писать.

резко

Забыл слово "уверенно". Подбирал по значению "не оставляя простора для возражений, градаций; деля на 'настоящих' и 'ненастоящих'".

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

Если вам интересно по поводу С++ , то у меня есть статистика по C#, например, что C# намного проще чем С++ (я знаю людей которые отказались писать на С++, при этом были счастливы писать на C#). И с точки зрения того что он проще он значит и лучше чем С++, но только относительно простоты(которая кстати тоже сложное понятие). Тем не менее интегрально, так сказать, С++ все таки будет лучше, мне кажется.

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

Он был удобным. А не вот этот вот зоопарк.

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

Ничего не мешает и сейчас писать на "си с классами".

В “С с классами” сейчас нет большого смысла, а вот сделать С++ с нормальным синтаксисом может быть и выстрелит.

Что такое нормальный синтаксис? Примеры?

понятный и естественный для пользователей исходного языка.

Мне понятен и естественен синтаксис плюсов. Вы, конечно, можете привести пример типа []()}[{())(*) (условно, просто для примера), который я не пойму, но это будет из разряда Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo.

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

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

Синтаксис C++ вполне понятный и естественный, но крайне сложный. А вот например синтаксис Python легкий, естественный но не всегда понятно, что происходит внутри

В смысле "никто не мешает?" Открываешь чужой проект, к которому нужно пристыковаться, а там в коде больше двоеточий, чем было дырок на перфокарте.

Он был ничерта не удобным. Вместо шаблонов - гигантские макросы или вообще внешняя кодогенерация. Или копипасты.

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

Или плодили классы

Он был удобным. А не вот этот вот зоопарк.

Никто не заставляет вас этот зоопарк использовать, хотите как в старые добрые — "-std=c++98, и танки наши быстры".

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

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

pub fn sort<T: Ord>(slice: &mut [T]) {
    todo!();
}

static_cast - Преобразование указателей/ссылок в иерархии наследования вверх (к базовому классу) и вниз (к производному), но без проверок во время выполнения (вы должны быть уверены в типе сами). Вызов явных конструкторов или операторов преобразования.

dynamic_cast - Безопасное преобразование во время выполнения. Единственный каст, который работает во время выполнения программы. Если преобразовать указатель не получится - вернет nullptr. Если преобразовать ссылку не получится - выбросит исключение std::bad_cast. Работает только с полиморфными классами (у которых есть хотя бы одна virtual функция, обычно деструктор).

const_cast - Единственный каст, который умеет убирать (или добавлять) модификаторы const и volatile. Например, когда вы вызываете старую C-функцию, которая принимает char* (неконстантный), но вы уверены, что эта функция не будет менять данные, а у вас есть только const char*.

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

bit_cast - Появился в C++20 как альтернатива опасному reinterpret_cast. Он не переинтерпретирует указатели, а создает НОВЫЙ объект, полностью копируя биты старого в новый.

Зачем вы, не желая даже 5 минут погуглить, лезет со своим Rust? При чём тут он? Здесь другой язык, с более чем понятной и удобной логикой

Это было к:

Компилятор физически не мог сказать «вы нарушили требование X», потому что никакого X нигде не записано и мог только дотащить вас за шиворот на строку 4212 в недрах и показать, что там для вашего типа не определён operator<, а чтобы вы поверили, то приходится выложить весь стек инстанцирования по дороге.

Для этого есть концепты в языке

Эти девелоперы на ×-расты еще и очень нервные, кстати. Видать, их надмозговый язык отнимает все силы - чуть что - сразу минусуют.

Тест простой - "Ты что, девелопернараст"?!

зря вы так, языки бывают двух типов: которые ругают, и на которых не пишут.

с более чем понятной и удобной логикой

5 кастов, и 1 из них депрекейтед. Очень удобно и понятно, хороший язык

pub fn sort<T: Ord>(slice: &mut [T]) {

Хе-хе )

Есть (до сих пор) такой замечательный язык perl, который замечателен в том числе тем что можно написать так:

foreach my $var (@arr){
  $var = $var + 2;
}

Даже если вы не знаете язык - можно догадаться что оно делает.
А можно так:

for(@arr){$_+=2;}
$_ += 2 for @arr;
@arr = map { $_ + 2 } @arr;

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

А это значит, что рандомный "новый программист" плюнет, и возьмет что ему попроще и попонятнее с детства, например, python.

И вот теперь мы видим вот то, процитированное, в Rust - тоже "коротко и понятно" /s

Как и приведенная конструкция в C++ для получения рандомного числа от 1 до 100.

Такое отпугивает...

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

Ну разве что вот это знать надо.

Модифицируется, там обход массива поэлементно.

Кто мешает то же самое на плюсах?

template<std::comparable T, size_t N>
void sort(std::span<T,N> slice) {
  .....
}

void sort(auto& slice)
  requires requires {
    // нам нужно, чтобы над разыменованными итераторами был оператор <
    *std::begin(slice) < *std::begin(slice);
  }
{
  .....
}

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

я думаю все довольно точно сказано) однозначно в точку

Замечательный язык, хочешь, пиши как на расте, хочешь, как на перле, хочешь, пиши как на си.

Понять бы, как писать на c++

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

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

Процитированное это яркий пример астронавтики. «Касты зло». А то мы сами не знаем! Покажите мне программиста, который бы не смог спроектировать приложение без кастов. Однако, C++ в реальности не летает в вакууме космоса, он держится на таких вещах, как, например, WinAPI. Вызовы WinAPI с их WPARAM и LPARAM достаточно уродливы сами по себе. Имея необходимость с ними работать, я УЖЕ достаточно наказан жизнью. Но нет! Давайте сделаем их ещё уродливее! Зачем? Чтобы меня стошнило? Или они хотят, чтобы это воспитательное уродство удержало меня от вызовов WinAPI? Но тогда, простите, зачем мне C++? Тогда я бы писал на Шарпе. То же самое касается Direct3D, например. (Вернее, касалось, когда я последний раз на нём писал, а было это давно — не знаю, как там сейчас). И вообще COM. Короче, касты это суровая необходимость кода общаться с реальным внешним миром, а его всеми силами изолируют.

Смешнее этого только заявление Степанова, что он проектировал свою библиотеку так, чтобы поощрять программиста писать алгоритмы. Это как если бы вы шли в магазин за набором инструментов, а дома обнаруживали, что он состоит из заготовок отвёрток разной степени заточенности и шлифовального камня. Ну и что, что тебе надо было собрать мебель или разобрать телефон, и ты не можешь просто взять готовый инструмент для достаточно типовой задачи. Зато тебе будет удобно выточить себе любую отвёрточку!

Я всё жду, когда кто-нибудь добрый обернёт стандартную библиотеку во что-нибудь удобное. Чтобы вместе того же mt3999… я даже название его не помню, хотя пишу его регулярно… ну, как пишу — копипащу из ДипСика… писать random.GetRandomInteger(0, 100);. И плевать мне на несовместимость. Хорошее настроение важнее.

Насчет прагматизма... )
Я не настоящий программист - я научный работник.
Мне нужна была программка чтобы что-то там сделать со строками в большом файле, я попросил DeepSeek сгенерить и чтоб именно на Си, не используя С++, потому что я понимал , что в ++овском тексте я не разберусь. И... программка заработала сразу как надо, без всяких правок! Возможно потому что Си как раз более примитивный/низкоуровневый...

И… программка заработала сразу как надо

Не хочу вас расстраивать, но есть шанс, что так только кажется. Строки это объективно тёмная, запутанная материя (владение ими; правильный перебор; (не)фиксированность ширины; глифы, символы, кодепоинты и байты строки — в чём разница?; нули в качестве маркеров и хранение размеров; (не)обходимость +1 под буфер для каждой внешней функции; глубокое понимание Юникода; переполнения всех мастей и т.д. и т.п.), и написать на Си что-то стопроцентно правильно работающее со строками требует высочайших навыков. По крайней мере, я уже много лет гоняю строки на C/C++, но не рискнул бы поставить жизнь или безопасность на то, что у меня это получилось.

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

Я всё жду, когда кто-нибудь добрый обернёт стандартную библиотеку во что-нибудь удобное. Чтобы вместе того же mt3999… я даже название его не помню, хотя пишу его регулярно… ну, как пишу — копипащу из ДипСика… 

а что мешает самому обернуть под свои задачи за строк 10? Это же абсолютно элементарно делается. И потому и не добавлено в стандарт, потому что у всех разное получится под своё

Я всё жду, когда кто-нибудь добрый обернёт стандартную библиотеку

Обернёт стандартную библиотеку, а не одного лишь Мерсенна.

Зато я знаю, какой язык самый лучший :)

История рассудила.

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

Даже если родство сходу неочевидно =)

Хоть что-то в жизни сделал не зря - убежал с плюсов когда они стали слишком большими (в 2011м стандарте, точнее раньше, когда его обещали под именем 0x)

Хых. Как раз-то на 17 ну очень много всего сделано.

11++ была и есть мощнейшей оживляющей струей. Жаль только что на 26ом как-то всё под- заглохло. Херб Саттер не может уже, что ли. До сих пор шло хорошо.

Ваще я думал JS будет худшим языком)

Ну что Вы, право, это лучшее, что могло случиться с человечеством (с)

Судя по тому как МНОГО хорошего и очень сложного и вообще разного крутого софта написано на "этом плохом языке" - статья 100% про то, как кому-то что-то мешает танцевать.

ничего не мешает, пошел 27 год как танцую :)

Неплохо конечно автор развёл всех на холивар :DDD

автор писал про свою боль, чужой мне не надо :)

У меня есть версия, что статья написана специально для того, чтобы устроить перепись ниасиляторов фанатов старого доброго “Си с классами” и Qt головного мозга. По типу вот этого или вот этого комментариев.

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

Особенностью моей работы является то, что пока в одном проекте можно использовать фичи свежего стандарта, рядом обязательно будет проект, отстающий на один два стандарта. Например, сегодня работаешь на C++20, завтра вынужден откатываться до C++17 или C++14 (вот C++11 давненько не было, к счастью). И каждый раз сразу видно скольких хороших вещей тебя лишили. Это ощущалось всегда: и при переходе с C++20 на C++17, и с C++17 на C++14… Особенно при переходе с C++14 на C++98/03. Тут вообще как будто на другой язык переучиваться приходилось.

Тем удивительнее в 2026-ом слышать панегирики C++98/03. Даже не смотря на то, что дичи в современные стандарты добавляют изрядно.

В целом с текстом согласен, но вот это:

Rust по дизайну на порядок лучше. Стандартный компилятор, стандартная сборка, стандартный пакетный менеджер, никаких заголовочных файлов, лучшие что я видел сообщения об ошибках, нормальные значения по умолчанию, отсутствие неявных преобразований, нормальный UTF-8, sum-типы, и безопасность памяти на этапе компиляции.

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

Большое спасибо за статью! Теперь, если меня спросят, почему я не пишу на C++ - мне будет куда ссылаться.

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

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

Хрестоматийный пример сложной простоты это случайное число, и где нибудь в джаве или пайтоне вы просто напишете random.randint(1, 100) и пойдёте дальше кодить, но не здесь. Это слишком просто, чтобы быть правдой.

Я попытался это исправить типо-безопасной библиотекой.

А сам random_device стандарт разрешает делать детерминированным и на старом MinGW он годами выдавал одну и ту же последовательность при каждом запуске, то есть длинная корректная мантра в реальности и длинная и коррекная, но не везде работает, хоть rand() % 100 и короче и хотя бы работает.

Вроде Как я это исправил с помощью mt19937_64 и uniq_distribution. При стресс-тестах на set и unordered_set повторов (во всех смыслах) отловить не удалось.

Упоминая Rust, почему-то не вспоминают, как там решена задача обеспечения обратной совместимости. А ведь в контексте этой статьи это как раз самое интересное!

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

Если программист пишет на современном Расте (Rust 2024), ему в подавляющем большинстве случаев не нужно знать, что там было в Rust 2015.

В целом, вполне себе возможная стратегия и для будущих вариантов C++, в общем-то, на мой взгляд...

На практике C++ ровно так и используют. Трудовой коллектив договаривается до версии языка и кодстайла. Например, что пишут на C++20 и запрещают использовать то-нибудь из него (что-то слишком заумное) и заодно весь голимый си. На кодревью палкой по морде дают за странный код, и всего делов, все пишут на "нормальном" подмножестве. Про большинство ужасов из этой статьи разработчики и не слышали никогда и не услышат - всё это им не нужно и никак не мешает жить, этого в их жизни нет. Если это вырезать из языка и закрыть под флагом компиляции, никто и не заметит, что из языка пропала половина. Именно таким способом достигается тот факт, что перегруженный монструозный C++ никакой гирей для разработчиков не является, изучать всё это не обязывает и про это даже никто не слышал. Вы спросите - "как это не обязывает, вот достанется тебе старый проект...". Не достанется, на старый проект я не пойду просто. Точно такой же свободный выбор, как "не ходить в C++, а использовать Go/Rust". Страдают писатели компиляторов, но их боль никто не слышит, потому что их физически мало на планете.

Ну, по-моему, это всё же несколько другое.

Во-первых, "Бить по морде на кодревью" это не то же самое что "автоматически отклонять компилятором". Если нет автоматического отклонения, можно что-то пропустить. Ну, для этого, конечно, есть всякие линтеры и прочее, которые обеспечивают выполнение требований по стилю кода, но это менее эффективно, чем прямое отклонение компилятором.

Во-вторых, всем приходится изобретать свои "велосипеды", придумывая, какое подмножество языка разрешать, а какое запрещать. Это порождает фрагментацию экосистемы - и в смысле кода, и в смысле навыков сотрудников (устраивающийся на работу должен будет знать все варианты, которые могут прийти в голову собеседующему, например). Это же касается и использования внутренних библиотек, компенсирующих какие-то недостатки стандарта - про это в статье упомянуто, см. "// антипаттерн: «мне надоело писать static_cast»"; локальные руководства по стилю тоже порождают подобное, в общем-то, хоть и в меньшей мере.

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

Так что опыт Rust с его редакциями, на мой взгляд, для C++ мог бы быть полезен.

Традиционная ссылка на самый подробный разбор недостатков C++ (правда, на английском): https://yosefk.com/c++fqa/

К множественных примеров инициализации ещё можно добавить варианты с дедукцией и без дедукции типов.

std::vector a{a.begin(), a.end()}; // получится vector<decltype(asd)::iterator>
std::vector<int> b{a.begin(), a.end()}; // попытается скопировать инты

placement new тоже в списке не вижу

std::vector<std::byte> buffer(1024);
MyClass* ptr = new (buffer.data()) MyClass();
ptr->~MyClass(); // звать деструктор надо ручками

В качестве отдельной категории можно ещё вспомнить thread local инициализацию.

еще есть скрытый rvalue_cast, но о нем чуть ниже.

core guidlelines добавляют ещё narrow_cast для сужения более длинных типов к более коротким: narrow_cast<int32_t>(int64_t{}). Всё для демонстрации понимания намерения.

Забыли написать про главное отличие С++ от остальных вариантов. Он стандартизированный. А это часто является одним из обязательных требований к инструменту.

И это то. Чего нет у большинства альтернатив и убийц С++

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

Это к Свиридкину и @Andrey2008 лучше я не силен в UB, в смысле написать могу, но с починкой хуже

Итого:

  1. C++ худший язык программирования всех времён. Да, вот перечень его недостатков: 1, 2, 3, .....

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

summon @antoshkka

Куда только смотрит комитет по стандартизации C++!!! <sarcasm. no offence :) >

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

Во дни сомнений, во дни тягостных раздумий о судьбах моей родины, — ты один мне поддержка и опора, о великий, могучий, правдивый и свободный… (И.Тургенев)

Sign up to leave a comment.

Articles