Грокаем C++ – Telegram
Грокаем C++
9.37K subscribers
46 photos
1 video
3 files
579 links
Два сеньора C++ - Владимир и Денис - отныне ваши гиды в этом дремучем мире плюсов.

По всем вопросам (+ реклама) @ninjatelegramm

Менеджер: @Spiral_Yuri
Реклама: https://telega.in/c/grokaemcpp
Мы на TGstat: https://tgstat.ru/channel/@grokaemcpp/stat
Download Telegram
И это все компилируется!
Сможете сказать, откуда каждая скобка взялась?)
1🤯4110😁10👀5🔥3🎄3🥱1
​​Атрибуты лямбды
#опытным

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

В С++11 у нас появилась возможность указывать атрибуты для функции. Например:

[[nodiscard]] int ComplicatedCompute() {
return 2*2;
}

ComplicatedCompute();
// warning: ignoring return value of 'int ComplicatedCompute()',
// declared with attribute nodiscard


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

Ну это функции. А как же лямбды? Хочется и для них указывать атрибуты.

И атрибуты для возвращаемого значения лямбды завезли в С++23. Выглядит это так:

auto complicated_compute = [] [[nodiscard]] () { return 2 * 2; };

complicated_compute();
// warning: ignoring return value of 'main()::<lambda()>',
// declared with attribute 'nodiscard'


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

Одни скажут: "усложнение синтаксиса!". Другие скажут, что давно пора лямбды подтягивать ко всем возможностям обычных функций.

Тут как бы все просто: не хотите - не используйте. У лямбды и так полно опциональных обвесок, одним больше, одним меньше. Можно определить шаблонную лямбду и обвесить ее всякими концептами с trailing return type. И это будет страшный зверь. Можно сделать отдельный пост, как может выглядеть ультимативная лямбда.

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

Don't ignore. Stay cool.

#cpp23
21👍9🔥8😁3
Атрибуты везде
#опытным

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

Есть на самом деле 3 легальных места для навешивания атрибутов на функцию.

1️⃣ Перед типом возвращаемого значения:

[[deprecated]] int foo() { return 42; }


Тогда он работает при непосредственном использовании функции.

foo();
// warning: 'int foo()' is deprecated


2️⃣ После имени функции:

int foo [[deprecated]] () { return 42; }


В таком виде атрибут тоже применяется к самой функции.

3️⃣ После параметров:

int foo() [[gnu::deprecated]] { return 42; }


Тогда атрибут применяется к типу функции, а не к самой функции. Разница вот в чем:

int foo() [[gnu::deprecated]] { return 42; }

int main() {
foo(); // no warnings
using FuncType = decltype(foo); // use of type is deprecated
}


Обычный вызов функции прекрасно компилируется. Но вот использование типа функции через decltype помечается как устаревшее.

Причем gcc и clang по-разному интерпретируют эту ситуацию. Clang говорит, что gnu::deprecated нельзя применять к типам и игнорирует атрибут. Вот ссылка на годболт для интересующихся.

Соответственно, в лямбде в тех же местах можно ставить атрибуты:

auto complicated_compute = [] [[nodiscard]] () [[gnu::deprecated]] {
return 2 * 2;
};


Признавайтесь, знали?)

Have your own opinion. Stay cool.

#cppcore
👍24🤯1910🔥6
​​Атрибуты параметров функции
#новичкам

Атрибуты можно также применять к параметрам функции. Это помогает чуть полнее в коде функции аннотировать некоторые свойства параметров.

class Interface {
public:
virtual void method(int param) = 0;
};

class Implementation : public Interface {
public:
void method(int param) override {
// this implementation doesn't use param so mark it
}
};


Вы определили какой-то интерфейс с методом, принимающим один параметр. И в какой-то момент появилась необходимость создать наследника, реализующего этот интерфейс, однако реализации не нужен параметр param. Возможно Implementation - это какой-то мок, у которого в принципе пустая реализация.

Если вы активно используете варнинги компилятора и прочие линтеры, при попытке собрать такой код вы скорее всего увидите предупреждение/ошибку компиляции. Чтобы стало все чётенько, стоит пометить param атрибутом maybe_unused, тем самым явно указав компилятору, что параметр не используется намеренно. И проблема исчезнет.

Однако из стандартных атрибутов по сути имеет смысл использовать только этот самый maybe_unused.

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

Например:

size_t safe_strcpy(
[[gnu::nonnull]] char* dest,
[[gnu::nonnull]] const char* src,
size_t dest_size
);


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

Annotate your code. Stay cool.

#cppcore
21🔥15👍9🤯1
​​Множество атрибутов
#опытным

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

1️⃣ Списочный. Внутри одних скобок перечисляете все атрибуты:

[[gnu::always_inline, gnu::const, gnu::hot, nodiscard]] int f();


2️⃣ Многоскобочный. Для больших любителей распиленных квадратов. Очень больших:

[[gnu::always_inline]] [[gnu::hot]] [[gnu::const]] [[nodiscard]] int f();


Больше квадратных скобок!

Также если вы используете несколько атрибутов из какого-то одного неймспейса, то можете использовать директиву using:

[[using gnu : always_inline, const, hot]] [[nodiscard]] int f();


Но тогда котлеты отдельно, мухи отдельно. Все атрибуты одного неймспейса нужно уносить в отдельные скобки. Это фича С++17.

Что интересно, вы можете написать полную чупуху:

[[rust, will, replace, cpp]] int f();


И это скомпилируется! Стандарт поддерживает любые implementation-defined атрибуты. Причем неизвестные атрибуты просто игнорируются. Правда игнор спровождается варнингами, которые тем не менее можно скрыть опциями, подобным -Wno-attributes.

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

Love squares. Stay cool.

#cppcore #cpp17
🔥2113👍10👎1
Яндекс приглашает на встречу РГ21 С++ 15 декабря в Москве

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

В центре встречи — выступление Антона Полухина, руководителя группы разработки общих компонентов в Яндексе. Он поделится новостями с последней встречи международного Комитета по стандартизации, расскажет о прогрессе в работе над С++26 и ответит на вопросы о том, как российским разработчикам участвовать в развитии стандарта языка.

Когда: 15 декабря, 18:30
Где: Москва, офлайн + онлайн-трансляция

Регистрация на встречу
🔥94👍4
Парсим ужас
#новичкам

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

[[]][[]]int main()[[]]{{[][][[]][[]]{{{}}}(main);}}


Если включить clang-format, то код преобразится во что-то такое:

/*1*/[[]][[]] int main()/*2*/[[]] {
{
[]/*3*/[[]](/*4*/[[]] /*5*/auto [])/*6*/[[]][[]] {
{
{}
}
}/*7*/(main);
}
}


Давайте посмотрим, откуда так много скобок:

1️⃣ Перед типом возвращаемого значения main определены 2 пустые области для указания атрибутов функции main.

2️⃣ Перед телом функции main определена пустая область для атрибутов, применяемых к типу функции main.

3️⃣ После блока захвата лямбды определена пустая область для атрибутов, применяемых к самой лямбде.

4️⃣ Внутри списка параметров лямбды определена пустая область для атрибутов, применяемых к единственному параметру лямбды.

5️⃣ Сама лямбда является generic и принимает массив неизвестного типа.

6️⃣ Перед телом лямбды определены 2 пустые области для атрибутов, применяемых к типу лямбды.

7️⃣ Вызываем лямбду с помощью указателя на функцию main.

8️⃣ Ну и разбавили это дело несколькими лишними скоупами по пути.

Не так уж и сложно оказалось)

Так, новичковая часть закончилась.
#опытным

Интересно, что этот код компилируется на gcc, но не на clang.

cppinsights показывает, что лямбда раскрывается во что-то такое:

class __lambda_5_17 {
public:
template <class type_parameter_0_0>
inline /*constexpr */ auto operator()(auto *) const {
{ {}; };
}

private:
template <class type_parameter_0_0>
static inline /*constexpr */ auto __invoke(auto *__param0) {
return __lambda_5_17{}.operator()<type_parameter_0_0>(__param0);
}

public:
// /*constexpr */ __lambda_5_17() = default;
};


То есть по факту мы имеем шаблонный оператор с auto параметром.

Как интерпретировать эту штуку - дело нетривиальное и по ходу компиляторы это делают по-разному. Видимо gcc при попытке инстанцировать шаблон с параметром int() выводит auto как тот же самый тип функции int() и в итоге лямбда принимает указатель на функцию. А clang при попытке инстанцировать шаблон выводит тип параметра функции как массив функций int() и не может принять main в качестве такого параметра.

Пишите ваше мнение, кто прав, кто виноват)

Deal with horrible things step by step. Stay cool.

#cppcore #compiler
❤‍🔥22😁139👍3🔥2
​​Мок собеседования
#новичкам #опытным

Представьте, что вы вкатун в АйТишку плюсовую. Прочитали несколько книжек, прошли кучу бесплатных курсов и дописали свой первый велосипед пет-проект.

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

Но как узнать, как проходить собесы, если никогда их не проходил?(оставим за скобками вопрос, как вообще добраться до собеса, это то еще шаманство)

Для этого существуют мок-собеседования. То есть дословно "имитация" собеседования.

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

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

В сети лежит куча готовых мок-собеседований по С++ на позиции разных уровней.

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

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

- Ambushed raccoon
- Владимир Балун

Ну а мы за вас собрали подборку всех(или почти всех) мок-собесов на русском языке по С++ и разбили их по уровням.

Junior:

- https://www.youtube.com/watch?v=_-EkLLZ5svk
- https://www.youtube.com/watch?v=H1mIHJxnm9E
- https://www.youtube.com/watch?v=PQ1C_0EAHFI
- https://www.youtube.com/watch?v=BCpHj698D8U
- https://youtu.be/7g8HufwNa0g?si=XVKRsuoHN20MJx3i
- https://youtu.be/a18qTcWn-II?si=OttfqKh0bHLjueOY
- https://www.youtube.com/live/rLOgkn6xVQA?si=lFqwGf_Wsr8IohHo
- https://youtu.be/VfoxaNLVtmQ?si=elt-OyZWB5tXp5hI


Middle:

- https://www.youtube.com/watch?v=nMdNehH8-Ss
- https://www.youtube.com/watch?v=Ed37R0FvkQ8
- https://www.youtube.com/watch?v=IDqMy4_xkb4
- https://www.youtube.com/watch?v=BOUEbS5L4-8
- https://www.youtube.com/watch?v=PwVMcxCBIkg
- https://www.youtube.com/watch?v=Np6UrKN6ZbA
- https://www.youtube.com/watch?v=1Ez3kbK_3bI
- https://www.youtube.com/watch?v=s6BXbEPaw5g
- https://www.youtube.com/watch?v=yfoFtu28n4o
- https://www.youtube.com/watch?v=cT3fonCyxJk
- https://www.youtube.com/watch?v=xwb2FAKxCUo
- https://www.youtube.com/watch?v=bOgz4K-ARzQ
- https://www.youtube.com/watch?v=wR4VRCp_BYo
- https://youtu.be/5enBKMwOST0?si=i-5mdyrxeeFiZKo1


Senior:

- https://www.youtube.com/watch?v=OwMEK_W8Ysw
- https://www.youtube.com/watch?v=dZpe58HKX-8

Просто вопросы и задачи с собесов:

- https://www.youtube.com/watch?v=boYk6gFg84E
- https://www.youtube.com/watch?v=ViHNB0_1j90
- https://www.youtube.com/watch?v=aYM7lksQ8yg
- https://www.youtube.com/watch?v=UdY_YMFx7SY
- https://www.youtube.com/watch?v=PStQ4jhhz08
- https://www.youtube.com/watch?v=wMYfg_iPqMQ


Просмотрев эти видосы(возможно по нескольку раз) и переписав ответы на все вопросы себе в тетрадочку или файлик, вы будете знать ответы на 95% устных вопросов, которые вам будут задавать в условной компании Х.

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

Ну а для любителем native english есть канал Кодингового Иисуса. Он постоянно у себя на стримах спрашивает у людей за плюсы. В основном люди валятся на простых вопросах, но иногда попадаются качественные собеседники. По крайней мере практика языка вам будет точно обеспечена.

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

Practice makes perfect. Stay cool.

#interview
54🔥18👍10❤‍🔥4🙏4
🎯 В C++ вызываемый объект — это намного больше, чем “функция”. За тридцать лет язык прошёл путь от простых указателей до мощных лямбда-выражений, которые лежат в основе современного параллелизма, асинхронности, алгоритмов и гибких архитектур.

На открытом уроке 18 декабря в 20:00 мск вы увидите эволюцию callable-подходов во всей широте: от function pointers и функторов до std::bind, std::function и современных лямбд. Разберёмся, что происходит «под капотом», как выбирать подходящий инструмент и почему именно лямбды стали стандартом промышленного C++.

Такой разбор особенно полезен тем, кто хочет писать более выразимый, модульный и безопасный код. Понимание callable-моделей даёт гибкость: проще проектировать API, реализовывать callback-механику, строить асинхронность, оптимизировать архитектуру приложения и мыслить “по-современному”.

⚡️Присоединяйтесь к открытому уроку в преддверие старта курса «C++ Developer» — получите базу, на которой строятся потоки исполнения, асинхронные модели и ключевые элементы современного C++20/23: https://otus.pw/cY5r/

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
6🔥4👍3🥱3
Как запустить cpp файл из консоли?

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

Следите за пальцами:

#if 0
g++ test.cpp -o test && ./test
exit 0
#endif

#include <iostream>

int main() {
std::cout << "Hello, World!" << std::endl;
}


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

1️⃣ Ядро смотрит на первые 2 байта файла и пытается найти там шебанг - #!. Если нашла, то в этой строчке будет указан путь до нужного интерпретатора.

2️⃣ Если шебанга нет и файл не является исполняемым(как у нас), то файл считается shell-скриптом и исполняется с помощью текущего командного интерпретатора.

3️⃣ Для shell-скриптов символ # обозначает начало однострочного комментария, поэтому первая строчка игнорируется.

4️⃣ Интерпретатор встречает команду компиляции и выполняет ее.

5️⃣ Теперь мы пытаемся реально скомпилировать этот файл с помощью g++. На этапе препроцессинга ветка условия #if 0 выбросится из текста файла и компилироваться будет только реально С++ код.

6️⃣ После компиляции интерпретатор запускает исполняемый файл и завершает работу на инструкции exit 0. Остальной С++ код он уже не увидит.

Напишите в терминале

chmod +x test.cpp
./test.cpp


И увидите заветные слова.

Дожили! Превратили С++ в питон...

PS: Большое спасибо за идею и материалы Даниилу @dkay7.

Have a fun. Stay cool.

#fun
53🔥20👍17😁13🤯11👀2🗿2🐳1
​​Bootstrap
#новичкам

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

Также возможно вы слышали, что компилятор java написан на java.

Если вдуматься в эти факты, то невольно задаешься вопросом: а как это вообще возможно? Нельзя же себя поднять за волосы из болота.

Вообще-то барон Мюнхгаузен смог и мы сможем!

Нужно лишь немного опоры. Чуть-чуть оттолкнуться.

Давайте пройдем весь процесс от и до.

1️⃣ Вы решили написать свой язык самый лучший язык во всей вселенной GOAT. Разработали синтаксис и правила языка, пару раз пооргазмировали от его крутости.

2️⃣ Дальше вам нужно уметь конвертировать код на новом языке в программу. Выхода нет: вы быстренько пишите простой компилятор или интерпретатор GOAT на уже существующем языке. Пусть для определенности вы написали компилятор на С. Компилируете его каким-нибудь gcc и у вас появляется первый компилятор языка GOAT. Это так называемый компилятор начальной загрузки или bootstrap компилятор.
Отлично! Вы можете писать программы на новом языке!

3️⃣ Но компилятор - это же тоже программа. Теперь вы можете на GOAT написать компилятор GOAT и скомпилировать его bootstrap компилятором. В итоге у вас получится рабочий компилятор GOAT, написанный на языке GOAT! Вот яйцо и родило яйцо.

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

Весь этот процесс называется bootstrapping.

Первый компилятор и jvm были написаны на С. Первый компилятор С был написан на ассемблере. Pascal'я - на Fortran.

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

Преимущества бутстраппинга:

👉🏿 Демонстрирует зрелость и самодостаточность языка. Можно развиваться самостоятельно и не зависеть от других технологий.

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

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

👉🏿 Упрощается разработка компилятора. Не нужно знать несколько языков.

А вот как бутстрапился С++ разберем в следующем посте.

Be self-sufficient. Stay cool.

#tools #compiler
39👍20🔥8
🔍 Вебинар перед курсом «Программист С»: разберём устройство памяти в С до мельчайших деталей

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

Урок для embedded‑разработчиков, системные администраторов
разработчиков на C++.

На вебинаре разберём:

- как устроен процесс на уровне секций памяти;
- чем отличаются стек и куча — и где лучше размещать данные;
- как ОС выделяет память и что происходит при вызове malloc();
- что делают системные вызовы brk()/sbrk();
- как free() «узнаёт», сколько памяти высвободить.

Что вы получите:
- чёткое понимание устройства стека и кучи;
- критерии выбора: когда использовать стек, а когда — кучу;
- практические навыки работы с динамической памятью — без утечек и ошибок.

Почему это важно?
Без понимания механизмов управления памятью даже опытный разработчик может столкнуться с:
- утечками памяти;
_ segmentation fault;
- непредсказуемым поведением программы.

📅 Дата: 15.01.2026 в 20:00

👉 Регистрируйтесь сейчас: https://otus.pw/71NX/

Вебинар проходит в преддверии старта курса «Программист С» и для тех, кто рассматривает обучение, мы подготовили специальное предложение. 🎄 Учитесь в новом году по старым ценам!
Максимальная скидка 30% на обучение до 21.12.2025:
1 курс — тающая скидка 15% до 21.12
2 курса −25%, 3 курса −30%
Вы можете оставить заявку на странице курса. Учиться системно — выгоднее!
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍4🔥4
​​История компилятора С++
#новичкам

С++ начал свой путь в 1979 году, когда Бьерн Страуструп работал над своей диссертацией. Он назвал его "С с классами". Это была довольно примитивная версия С++, были просто добавлены классы, их методы, возможность наследования и другие более минорные фичи. Никакого полиморфизма и шаблонов. Для компиляции этой версии было достаточно написать препроцессор для С компилятора, который бы транслировал инструкции С++ в С-шный код. Этот препроцессор + С-компилятор назывался CPre.

Но Бьерн не забросил свою разработку и в 1982 году разработал улучшенную версию С с классами и назвал ее С++. Она уже включала виртуальные функции, перегрузку функций и операторов, ссылки, стандартные функции работы с памятью(new/delete).

И CPre уже не справлялся с такими мощными фичами. Да он и изначально был довольно костыльным, потому что не было работы с типами, которой быть и не могло на этапе препроцессинга. Нужно было делать лексический и синтаксический анализ, строить ast дерево программы, чтобы поддерживать типобезопасность.

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

По сути нужен был полноценный компилятор, преобразующий С++ код в С. И им стал Cfront.

Вот теперь пошли интересности.

Первая простейшая версия языка С++ и компилятора Cfront была написана на "С с классами" и скомпилирована CPre. В дальнейшем мощные фичи в язык добавлялись уже с помощью Сfront, который писался на предыдущей версии С++. CPre не вывозил возросшей сложности.

Ну хорошо, Бьерн у себя на домашнем компьютере с определенной архитектурой имеет свежайшую версию Cfront, которая написана на С++. С++ уже не может быть скомпилирован CPre. Как получить компилятор на другой архитектуре?

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

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

Cfront успешно развивался еще несколько лет, но у него были объективные недостатки:

👉🏿 скорость компиляции в С/С++ и так не славится скорость, так тут еще и двухступенчатая компиляция была

👉🏿 при попытке компиляции ошибки показывались в сгенерированном C-коде, а не в исходном C++, что затрудняло отладку

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

В итоге при попытке добавить в язык исключения Cfront умер...

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

The end.

Know that everything has its limits. Stay cool.

#tools #compiler
26👍10🔥5🤷‍♂2