ru
Feedback

Не попадитесь на канал ботавода! Telemetrio находит и помечает такие каналы 👉 Хотите видеть метку — оформите подписку

this->notes.

this->notes.

Открыть в Telegram

О разработке, архитектуре и C++. Tags: #common, #cpp, #highload и другие можно найти поиском. Задачки: #poll. Мои публикации: #pub. Автор и предложка: @vanyakhodor. GitHub: dasfex.

Больше
4 490
Подписчики
-424 часа
+27 дней
-1530 дней
Архив постов
#cpp #common Коллеги выпустили YTsaurus в опенсорс. Это крутое распределённое хранилище, на котором у нас держится примерно много всего. https://github.com/YTsaurus/YTsaurus https://habr.com/ru/company/yandex/blog/721526/

#cpp #common 1. Прикольный доклад Andrei Alexandrescu про мысли о рефлексии в плюсах. 2. В комментах поста про кодировки упоминали про сложность устройства эмодзи. Вот хороший пост про это. У автора оригинала кстати есть канал, где он горит на всякое время от времени: @nikitonsky_pub. 3. Не самый свежий доклад, но довольно общий и широкий. Про то, что особенного в субд в оперативной памяти. 4. В дополнение к посту про инициализацию можно почитать две статьи про std::initializer_list: раз, два.

#nereklama У нас в Лавке активно развивается b2b направление (это когда мы предоставляем возможность пользоваться нашими техническими решениями партнёрам), в связи с чем ребята семимильными шагами расширяют команду. Потому есть пару вакансий на C++ и python: - руководитель команды разработки оркестратора в b2b Лавки; - руководитель команды разработки логистики в b2b Лавки; - разработчик на python в команду инфраструктуры b2b Лавки; - разработчик на C++ в команду b2b Лавки. Я работаю в Лавке уже примерно полтора года и словил сильный буст в хард- и, что довольно важно, софтскиллах. Успел запилить несколько крупных фич, которые имеют ощутимое влияние на пользователей и внутренние процессы. А ещё менторил ребят, собесил, участвовал в яндексовых образовательных проектах и выступал с рассказами на внутренних митапах. В общем, вижу и трогаю множество возможностей для развития в разные стороны. Вы тоже можете : ) Можно подаваться напрямую по вакансиям или написать мне в лс (@vanyakhodor) за рефером.

#cpp Чуть-чуть из доклада Daisy Hollman (не путать с Nadin Holman) на CppCon 2022 (прошлый я упоминал тут). Fun fact. Можно скрыть общий шаблон с помощью constraint’ов: template <class T> struct B { void print() { cout << “unreachable”; } }; template <> requires true struct B<T> { void print() { cout << “new default”; } }; Теперь изначальный шаблон недостижим, потому что более частный с точки зрения компилятора предпочтителен, пусть семантически он ровно такой же. По факту, вы можете полностью заменить определения каких-нибудь стандартных классов (на практике это вероятно уб, но теоретически да). Правда если вы потом захотите сделать ещё одну специализацию: template <class T> requires std::integral<T> struct B<T> { void print() { cout << “integral”; } }; то для условного int получится неоднозначность, потому что по факту это выбор между двумя requires true. Но это можно обойти объявлением всегда истинного концепта: template <class T> concept True = true; И поменять сигнатуры на template <class T> requires True<T> // и template <class T> requires std::integral<T> && True<T> Это какие-то жоские преки. Словно про поломанность std::void_t вспомнил. Второй fun fact (ну это конечно чуваки крутят-вертят как хотят уже). Помним можно писать user-defined literal templates: template <char… Chars> constexpr auto operator “”_i(); И использовать их можно вот даже для чисел: auto x = 123_i; Компилятор распарсит 123 в три символа и внутри оператора можете творить что хотите. Собственно на этом и построена возможность обращаться к элементу тупла через operator[]: template <size_t I> struct index {}; // преобразовываем литерал в число template <char… Chars> constexpr auto operator “”_i () { return index< size_t rv = 0; for (auto c : {Chars…}) rv = rv * 10 + (c - ‘0’); }()>{}; } template <class… Ts> struct index_tuple : std::tuple<Ts…> { using std::tuple<Ts…>::tuple; template <size_t I> auto operator[](index<I>) { return std::get<I>(*this); } }; auto t = index_tuple<int, double>{1, 2.2}; cout << t[1_i]; Ну и всё. Файная обёртка над std::get готова😁 Но помните, что это работает только для литералов. Прикона конечно, но кринж, что такие простые хотелки решаются вот такими методами. Может на раст уйду. Хз.

#common Базово про кодировки. Очень важно различать два основных понятия: текст и его представление в виде байт. Есть две основные операции перевода из одной сущности в другую: из текста в байты с помощью какой-то кодировки (обычно называется encode), и наоборот (decode). Собственно кодировка это способ/правило/функция отображения текста в байты и наоборот. Одной из первых адекватных кодировок стала ASCII. Она в соответствие каждому символу ставит семибитовый код (т.е. хранится 128 символов). 32 символа - управляющие, остальные - стандартные английские буквы, цифры, знаки препинания и т.д. 8й бит использовался для контроля ошибок. Позже она была расширена до 256 символов (задействованы все 8 битов). Однако уже успели появиться другие однобайтовые кодировки, которые расширяли изначальные 128 символов. Например KOI-8 — кириллическая кодировка, имеющая интересную особенность: в случае, если используемый редактор не поддерживает эту кодировку, при преобразовании кириллических символов в ASCII получался транслит; соответственно русские буквы шли не по алфавиту. Или CP866 — кодировка, используемая в MSDOS, — и CP1251 — дефолтная кодировка при использовании кириллицы в windows до windows 10. Со временем 256 символов конечно же перестало хватать. И наличие различных кодировок не является универсальным решением. Потому родился Unicode. Он также является отображением из кода в символ, только размеры чуть больше: 2^21 символов. Unicode реализует несколько основных кодировок: - UTF-32. Каждый символ занимает ровно 4 байта. С ней легче всего работать, но она занимает больше всего памяти из-за фиксированного размера. - UTF-16. Каждый символ представлен либо 2, либо 4 байтами. - UTF-8. Символ кодируется 1, 2, 3 или 4 байтами. ASCII-часть кодируется одним байтом, причём коды символов полностью совпадают. Устройство UTF-8 (наиболее популярная из вышеназванных). Если говорить об ASCII-символе, то в начале идёт 0, а потом 7 бит символа. Если символ не ASCII, тогда нужно понять, сколько байт нужно для представления символа. Для этого в самом первом байте вместо нуля пишется некоторое количество единиц. Это количество и указывает на то, сколько байт занимает символ. Т.е. если он занимает 3 байта, то пишется 111, после чего [что логично] идёт 0. Далее идут уже значащие для кода биты. Каждый последующий байт начинается с 10, после чего идут 6 значащих битов. Зачем нужна такая избыточность 🤔 ? При разработке кодировки требовалось сохранить обратную совместимость с уже большой кодовой базой, написаной на C, в котором нулевой символ является знаком окончания строки. Как видим, нулевой символ в UTF-8 только один. Ещё тут выполняется такое свойство, что ни один символ не является префиксом другого, что позволяет использовать некоторый набор старых функций, работающих с ASCII с UTF-8 (например поиск подстроки в строке). Собственно если посчитать, сколько символов мы можем задать подобным образом, получится что-то около 2^21. Вот хорошая статья про это всё.

#cpp 1. Женя писал про попытку закоммитить в clang реализацию assume и про то, почему не получилось. Даже как-то и несмешно уже. 2. А Саша писал у себя про self-move. Почитайте. Там ещё в комментах есть интересное. 3. Тут краткий и понятный поинт про то, почему лучше использовать std::make_unique вместо прямой передачи указателя в конструктор std::unique_ptr. В целом понятно. А тут можно посмотреть аналогичный ответ про std::make_shared (разница гораздо более ощутимая, но не такая однозначная). 4. Небольшая статья про использование aligned new. 5. Доклад Herb Sutter про видение обобщённого программирования на C++ аж из 2017ого, вполне, тем не менее, актуальный, т.к. многое из рассказанного ещё не завезли. 6. Небольшая статья с заметками про лямбды. Такой вот минипостик получился.

#cpp Сходил на встречу российской РГ21. Накидаю инфы (что-то вы уже видели, но лишним, думаю, не будет). В C++23: - починили некорректные срабатываения static_assert(false): if constexpr (constexpr_condition) { … } else { static_assert(false, “message”); } - починили range based for. Link на proposal; - добавили static operator[] (в записи приконый пример с таким оператором для енамов); - монадические операции для std::expected; - дополнения к алгоритмам ranges (например эффективная итерация по ключам/значениям мап и много другого), но из-за этого ренджи из 23его стандарта сильно несовместимы с ренджами из 20ого. Не оч понятно, что это значит. Подождём и потрогаем; - std::stacktrace и std::print; - constexpr много всего (например std::unique_ptr). С++26: Пока глобально планируется примерно вот так: - library support for coroutines; - executors; - networking; - pattern matching; - reflection; - и всякие другие штуки. Конкретно: - #embed (емнип приехал из C23); - пытаются пропихнуть std::get для агрегатов; - stacktrace for exceptions в комитете приняли с вопросами, будут поправлять; - ABI ломать пока не будут, не за чем, говорят. Хех. Прикиньте, С++23 это вот в этом году. А мы только недавно на 20й стандарт перешли🥴🥴🥴

#highload #cpp #go Досмотрел всё интересующее из майского highload, так что докидываю к прошлому посту (и пару докладов с плюсовых конф). 1. Как мы готовили поиск в Delivery Club. Иван Максимов (link). Тут интересно, потому что я много работаю над штуками вокруг нашего поиска (не непосредственно качеством, а именно около). Когда ты чуть-чуть в теме, смотреть интереснее, что там чуваки намутили. 2. Хайлоад, которого не ждали. Дмитрий Самиров (link). Тут аналогично, потому что чуваки намутили какую-то доставку продуктов из Я Еды. Почти Лавка. Только у нас круче. 3. Как мы устроили переезд 10+млн строк С++ кода на новый стандарт. Никита Старичков (link). 4. Статический анализ кода++. Анастасия Казакова (link). 5. Компилируем 200 000 файлов быстрее, чем distcc. Александр Кирсанов (link). Это интересно с точки зрения, что в ВК намутили миллион каких-то своих способов компилироваться из PHP в C++ эффективнее. И по чуть-чуть чуваки эту свою инфру развивают. Прикольно посмотреть, что нового они там придумывают. 6. Почему всё сложно с оптимизацией и что с этим делать. Константин Шаренков (link). В целом доклад с вопросами, но мб вам зайдёт. Мне не то чтобы прям, но выше среднего. 7. Go Map Internals. Егор Гришечко (link). И плюсовые из закромов: 8. C++14 Reflections Without Macros, Markup nor External Tooling. Antony Polukhin (link). Блин, оч крутой доклад с CppCon 16ого года, который я как-то пропустил. Антон рассказывает про magic_get/boost::pfr и всякие мозговзрывающие штуки, которые там использовались. 9. Back to Basics: C++ API Design. Jason Turner (link). 10. Метаклассы в C++17: фантастика? Реальность! Сергей Садовников (link). Мб вам что-то на последних конфах понравилось? Можно поделиться в комментариях.

/155

#common #highload #cpp Пару дней назад вышли записи прошломайского Highload++ Foundation 2022. Накидаю докладов из того, что уже посмотрел. 1. Как выжать 1 000 000 RPS. Андрей Аксёнов (link). Классический доклад в своей нестандартной манере про бенчмарк Sphinx. 2. Поиск GPS-аномалий среди сотен тысяч водителей. Александр Царьков (link). 3. Как мы ускорили Яндекс Go на несколько секунд. Денис Исаев (link). Я немного упоминал этот доклад в /155 п.4. Просто и прикона. 4. Экскурсия в бэкенд Интернета вещей. Владимир Плизга (link). Прикона было узнать, какие задачи в целом решают с помощью IoT. Правда я так до конца и не понял, что это такое. 5. Авито: root cause detector. Юрий Дзюбан (link). Интересная попытка автоматизировать расследование проблем. 6. Избавляемся от кэш-промахов в коде для x86-64. Евгений Буевич (link). Я не поклонник вот этих низкоуровневых штук, но может вам будет интересно. И заодно тогда можно глянуть LLVM Optimization Remarks by Ofek Shilon с осеннего CppCon 2022 (link). 7. Как наладить CI-процесс в монорепозитории. Алина Власова (link). 8. Настраиваем инцидент-менеджмент: от хаоса до автоматизации. Сергей Бухаров (link). Тут скорее было интересно посмотреть, что у других. Имхо, у нас лучше. Основные вопросы к системе это: насколько нужен скрайбер; почему оценкой импакта на бизнес занимаются разработчики в процессе инцидента; и зачем есть secondary дежурный, почему их только двое?

#cpp Посмотрел доклад Andrei Alexandrescu на CppCon 2021. Накидаю приконых фактов оттуда: - предлагает засунуть в стандарт template <typename… Ts> struct type_sequence {}; потому что если этого не сделать, каждый будет писать это сам (и вероятно с помощью tuple/variant, что концептуально не всегда то, что нужно). В таком виде со списками типов предлагали работать и на CppRussia 2019 с примерами других функций, которые могут понадобиться при работе со списками типов, что выглядит вообще-то довольно приятно и в ногу со временем. И рядом Alexandrescu предлагает три базовые функции в классической манере для работы с металистом типов: head, tail и cons (создать лист из элемента и другого листа). Всё как в Lisp. - и депрекейтнуть integer_sequence, потому что это не последовательность целых чисел, а последовательность значений какого-то типа. Вместо него ввести value_sequence, потому что это семантически честнее. Из интересного ещё можно отметить такое. Предположим есть функция template <typename T, typename… Ts, typename U> int f(T, U, const Ts&…); и её вызовы f(1, 2); // Ts=<> f<int, int>(1, 2, 3); // Ts=<int> f<int, int, double>(1, 2, 3, “4”); // Ts=<int, double> Тип U всегда сможет вывестись! Причём из параметра, а не указанного списка типов в шаблоне. В первом случае это int по понятным причинам. Во втором это тоже int. В третьем же Ts выводится как <int, double>, а U — исходя из параметра. Причина в том, что если variadic pack начал матчится, он не остановится и идёт до конца (жадный матчинг). Получается, в таком кейсе у нас нет возможности указать тип U компилятору явно, ведь он всегда будет выводится из параметра. Соответственно, если у вас есть что-то такое template <typename... Ts, typename T = int> T f(Ts…); тип T может быть только int, потому что в него никогда ничего не сматчится. Соответственно, если у вас пак типов функции находится не в конце, то он всегда должен быть указан явно: template <typename... Ts, typename T> int f(Ts... values, T value); f(1); // Ts=<> f<int, double>(1, 2, 3); // Ts=<int, double> f<int, int>(1, 2); // error, потому что не хватает одного аргумента Всё это приводит к забавным кейсам вроде такого template <typename... Ts, typename... Us> int f(Ts... ts, Us... us); Тут Us всегда выводится из параметром (deduced), тогда как Ts должно быть указано явно. Можете попробовать понять для себя, чему они будут равны в следующих случаях: f(1); f(1, “2”); f<int, char>(1, ‘2’); f<int, char>(1, '2', “three”); Или может быть такой угар: template <typename... Ts, typename... Us, typename T> int f(Ts..., Us..., T); При любом вызове Us будет пустым паком. Что бы вы не делали. Все компиляторы это кушают, но стандарт говорит, что если один пак всегда пустой, то это no diagnostic required (грубо говоря, нельзя так). Ещё есть такие штуки как std::tr2::bases и std::tr2::direct_bases, которые возвращают тайплист всех базовых классов указанного и все непосредственные базовые классы указанного соответственно. Хотя вообще доклад не об этом : ) Так что посмотрите.

#common Ещё немного полезностей из личного опыта. Ещё когда в начале второго курса в каком-то рандомном курсе по python я столкнулся с регулярками, подумал, что это оч крутой инструмент, который, тем не менее, вряд ли часто применяется (потому что так с большинством знаний, к сожалению). Как же я был глуп. Сейчас я пользуюсь ими чуть ли не каждый день, пусть часто и хватает какого-то минимального базового набора. Ниже небольшой список примеров, где они пригодились: - я не оч люблю codesearch в ide, потому что сижу в clion, который, как известно не самый быстрый (в том числе потому что у него проблемы с индексацией мало-мальски больших проектов). У меня это скорее прокачаный блокнот, по которому я делаю только Ctrl+F в рамках файла. Но когда надо поискать что-то в более глобальной окрестности, я пользуюсь внутренним сервисом для поиска по монорепе. И он поддерживает регулярки (как для строк, так и имён файлов), что вообще-то довольно удобно. Кажется, недавно github обновил у себя поиск и можно делать что-то похожее. - во внутреннем userver, в отличие от опенсорсного, есть кодогенерация (статья на хабре, где про это можно почитать), которая позволяет не писать бойлерплейт для раскладывания полей в структурки и обратно. Ты просто задаёшь схему в формате yaml, по которой валидируется реквест/респонс. И там есть возможность для строковых переменных задавать паттерны регулярками, по которым так же будет проводиться валидация. Если что-то не прошло, клиент получает 4хх автоматически. - иногда в поиске бывают примеры не самой релевантной выдачи, которые обусловлены особенностями реализации. И иногда их проще закрыть чем-то вроде блеклиста, в котором можно поюзать регулярки для обобщения каких-то кейсов и неразрастания блеклиста. Недавно обходил деревья dfs’ом, что вызывает какие-то детские эмоции радости, потому что после десятков (и может даже сотен) раз, когда он писался для спшных тасок, это знание перестало быть бесполезным с точки зрения применения в проде. Рядом у нас ещё есть много разных bfs’ов для разных кейсов. А когда-то видел bfs для специфического обхода какой-то графической сетки. Короче алгоритмы на собесах даже как-то обоснованы (даже k раз приходилось делать что-то с двумя указателями). В статье про обработку ошибок я упоминал, что не нужно ловить всё подряд (catch (…) {} или ловить std::exception). Опровержений первому я пока не увидел, но ловить std::exception оказалось полезно. Кейс примерно такой: мы умеем явно ловить только ответы ручек от других сервисов, которые описаны в api, но это не всё, что сервис может ответить, т.к. иногда есть неописанные 5xx или технические 429/другие 4хх. И хочется, чтобы неуспешный запрос не влиял на доступность сервиса, который этот запрос и делает. Но мы согласны деградировать некоторую функциональность. Тут как раз хорошим вариантом будет ловить std::exception. ============================ Сейчас верхнеуровнево пытаемся намутить курсов на новый семестр, примерно с нуля. Занимает оч много времени (из-за чего я тут притих). Но есть ощущение, что получится даже не очень плохо. Может попозже чем-нибудь в этом месте поделюсь.

В этом году я начал пытаться писать сам время от времени вместо тупого вкидывания ссылочек на разные материалы, просто потому что так полезнее и мне, и вам. Вспомним, что тут появилось за 2022й год (особенно полезно, если вы пришли на канал чуть позже и не видели чего-то), а после подведу какие-то итоги. Посты: - необычные возможности C/C++; - o chaos engineering; - об аллокаторах в других языках; - macros tricks; - о векторах; - о хеш-таблицах; - о вероятностных структурах данных; - о кешах; - про рефакторинг; - backend for frontend; - про ub; - впечатления от C++ Zero Cost Conf 2022; - деревья в алгоритмах; - про полезности из личного опыта; - немного из метапрограммирования; - пару кейсов, связанных с грамматикой языка; - про эпохи; - про cpp2; - статический лист для сборки динамических аллокаций; - чуть-чуть про софтскиллы; - о подходах к реализации дженериков (помню, что торчу пост про go); - про пагинацию; - про лямбды; - про компиляцию в общих чертах; - RVO/NRVO; - пачки пропозалов: раз, два; - пачки рандомных фактов: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14; О сборщиках мусора: - основные алгоритмы; - G1GC в java; - другие сборщики в java; - python gc; - сборщики в других языках; - о сборке мусора и C/C++. Статьи: - об устройстве популярных аллокаторов в C++; - об интересных структурах данных; - обработка ошибок и C++. Задачки: - про использование variadic templates; - хз про что это но вот. Примерно понимаю, о чём хочу написать ещё минимум одну статью и около миллиарда постов, так что контент в следующем году будет. Другой вопрос, что нагрузки становится всё больше, потому, вероятно, писать буду реже. Или чаще. Хз короч. Рандомные факты про жизнь. Закончил универ, прокачался на работе и в целом доволен тем, чем сейчас занимаюсь. Немного участвую в образовании тут-там. Хотя может и много. По крайней мере универа в следующем семестре будет явно больше, чем в некоторые семестры в студенческое время. Оч интересно и оч сложно. Завёл @dzikart и пытаюсь поддержать жизнь в @memesfromhole. Набил ещё три татухи и научился жонглировать (кек). Не умер, что особенно приятно. Временно на каникулы. Не болейте.

#cpp RVO/NRVO 1/2. Давно хотел рассказать про всякие оптимизации, которые делаются в плюсах. Тут будет пост про RVO/NRVO, а позже про что-нибудь ещё. Может в начале будут какие-то неявные допущения или грубые формулировки, но к концу поста постараюсь донести все детали, чтобы картина сложилась. Для начала про терминологию в стандарте. Формально есть общий термин copy elision, который в целом о том, чтобы не делать лишних копий объекта и создавать его сразу там, где нужно. Например ниже примеры copy elision: T t = T(); // not T() + operator= but only T(); T t = T(T(T(T(T(T(T(1))))))); // not T(1) + many T(const T&) + operator= but only T(1) Вот что говорит стандарт ([class.copy.elision]): When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. Т.е. компилятор в праве применять эту оптимизацию в обход copy-/move-кторов, даже если в них или деструкторе есть какие-то сайдэффекты. Так же тут есть 4 пункта. В первом говорится, что тип инициализирующего выражения и тип переменной с итоговым результатом должны совпадать. Это главное условие для RVO/NRVO. RVO/NRVO это частные случаи copy elision, которые происходят в результате возврата чего-либо из функции. Далее мы будем пользовать примерно таким классом, чтобы понять, что происходит с объектами: #define TELL(str) { std::cout << str << std::endl; } struct S { S() TELL("def") S(const S&) TELL("cp") S(S&&) TELL("mv") }; Так же есть такой термин как temporary materialization – принцип, согласно которому prvalue-выражение не создаётся физически в памяти до тех пор, пока оно не будет присвоено в не prvalue-объект. Return Value Optimization по сути и есть пример temporary materialization. Посмотрим на базовый пример (godbolt): S f() { return S(); } S s = f(); Тут у нас prvalue-выражение S(), благодаря чему мы можем не создавать переменную в момент его появления в return и присвоить создать лишь в s. И из всех интересных нам конструкторов будет вызван лишь конструктор по умолчанию. cv-квалификаторы в сигнатуре функции в данном случае нам ничего не портят (gb). А если мы можем (не)явно сконструировать возвращаемый тип из выражения в return? Добавим в S такой конструктор: S(std::string&&) TELL("str&&") и собственно пример (gb): S f() { return std::string(10, '1'); } S s = f(); Всё хорошо, даже не смотря на то, что тип в return и в сигнатуре не совпадают. По факту компилятор представляет выражение в return как S(std::string(10, ‘1’)), что тоже является prvalue. Даже в таком случае всё в порядке (gb): S f() { std::string s(10, '1'); return s; } Всё корректно. Т.к. компилятор справляется тут даже сделать неявный move переменной s. Причём это всё ещё RVO. Стоит понимать, что с C++17 RVO — гарантированная штука и даже не оптимизация. Давайте поймём, как оно работает. У компилятора есть адрес места в памяти, где должен оказаться результат выполнения функции (называется return slot). Когда выполняются условия для RVO, компилятор может сразу создать объект из вашего return в нужном месте в памяти. Т.е. это скорее про свойства инициализации объектов. Named Return Value Optimization уже оптимизация (причём не гарантированная, тут компиляторы делают кто что может). Базовый пример (gb): S f() { S s; return s; } Или пример посложнее, где в S появился int x (gb): S f() { S s; s.x = 1; return s; } void gg(S& s) { s.x = 1; } S g() { S s; gg(s); return s; } Компилятор всё ещё может создавать локальную переменную s в функциях сразу в return slot итогового объекта. Для NRVO правда volatile для локальной переменной всё портит (gb). Почему важно знать об этих штуках? Потому что NRVO можно сломать (gb): S f() { S s; return std::move(s); // return S&& } Теперь компилятора обязан создать объект и мувнуть его, потому что это то, о чём вы его попросили явно. Так же можно думать, что у выражения в return и в сигнатуре теперь разные типы (S&& и S).

#cpp 1. Хочется укрепить понимание того, что же такое модификаторы доступа (public, protected, private), потому что в последнее время вижу тут-там некоторое непонимание у молодых особей, изучающиъ C++. Это именно модификаторы доступа. Они всего лишь не дают напрямую обратиться к члену/методу класса. Но это не значит, что вы совсем не можете работать со скрываемыми объектами. class A { struct T {}; public: T getT() { return T{}; } }; // A::T t; // ce A a; // A::T t = a.getT(); // ce auto t = a.getT(); // ok В двух первых случаях мы напрямую пытались обращаться к типу, спрятанному за модификатором доступа (и не получилось). В третьем всё ок: мы не трогаем тип напрямую -> можем с ним работать. Причём тип лишь недоступен напрямую, но мы его видим снаружи (видимость и доступность это разные штуки). Так же модификаторы доступа это концепция времени компиляции, потому, если не думать, можно ловить неприятные штуки: struct A { virtual void f() = 0; }; struct B : A { private: void f() override { std::cout << 1; } }; A* a = new B(); a->f(); Вроде как f() в B помечена private, но т.к. это требование проверяется на компиляции, а виртуальные штуки работают в рантайме, будет успешно выведена единица. Кстати, интересно, могут ли статанализаторы такое трекать как говнокод🤔 Не пробовал. Но может вы потрогаете и расскажете. 2. В C++11 появились типы фиксированного размера. И почему-то стало принято юзать их вместо стандартных int, long long, и прочей братвы. Мне не очень понятно почему. Ведь вам на самом деле редко нужны типы фиксированного размера. Часто вам хватает знания, что конкретный тип удовлетворяет каким-то ограничениям сверху/снизу. И часто можно просто поставить какой-нибудь static_assert на то, что у вас CHAR_BIT равен 8 или сколько вам там нужно. Короч кмк это специфический инструмент, непонятно почему ставший popular. 3. Можно почитать Женин довольно понятный пост про ABI и почему его не хотят ломать (там много ссылочек с доп инфой).