Ноябрьская встреча в Сан-Диего — предпоследняя перед feature freeze. Какие новинки появятся в C++20, что из крупных вещей приняли, а что отклонили — всё это ждёт вас под катом.
char8_t
Добавили новый тип данных char8_t. Массив этих символов представляет собой UTF-8 строку:
std::u8string hello = u8"Привет, мир!";
// TODO: Вывести значение пока нельзя!
//std::cout << hello;
На первый взгляд кажется, что нововведение незначительное. Но это не так.
char8_t — первый шаг к тому, чтобы стандартная библиотека C++ из коробки поддерживала UTF-8. Впереди ещё много работы, к C++20 она явно не завершится.
Однако уже теперь char8_t многим превосходит char/unsigned char. Программы, использующие char8_t, могут работать быстрее за счёт того, что char8_t не алиасится с другими типами данных. Иными словами, модификация строки или любой переменной не приведёт к тому, что значения переменных будут перечитываться из памяти:
bool do_something(std::u8string_view data, int& result) {
result += data[0] - u8'0'; // переменная result изменилась
return data[0] != u8'0'; // будет использовано значение из регистра для data[0]
}
Если бы мы в примере взяли char (std::string_view), то получили бы код, в котором несколько раз обращаемся к BYTE PTR [rsi]. В случае char8_t это не происходит.
Учтите, что теперь u8«hello» — это массив chat8_t, а не массив char. Данное изменение в стандарте ломает обратную совместимость с C++11/14/17! Полный список изменений связанных с char8_t доступен в документе P0482. В этом же документе есть список примеров, когда валидный до C++20 код становится невалидным из-за char8_t.
constexpr
К C++20 многие (и я в их числе) хотят увидеть в стандарте контейнеры, которыми можно пользоваться на этапе компиляции. Это позволит делать меньше вычислений на runtime, а значит, при работе приложения будет тратиться меньше процессорного времени. При этом, зачастую, вам не придётся ничего менять в исходниках — всё просто начнёт работать быстрее, инициализироваться на этапе компиляции, лучше оптимизироваться компилятором…
В Сан-Диего сильно расширили возможности компилятора и стандартной библиотеки по вычислению выражений на этапе компиляции:
- try и catch теперь можно писать в constexpr функциях (P1002).
- dynamic_cast и typeid можно вызывать в constexpr (P1327).
- Добавили consteval-функции (бывшие constexpr!) — функции, которые можно вычислять только в constexpr контексте (P1073).
- Добавили функцию std::is_constant_evaluated(), возвращающую true, если в данный момент функция вычисляется на этапе компиляции P0595. Старайтесь без крайней надобности не пользоваться std::is_constant_evaluated(): она очень своеобразна.
- Менять активное поле union теперь так-же можно при constexpr вычислениях (P1330).
- Невообразимое множество классов и функций стандартной библиотеки теперь помечены как constexpr. Они смогут вычисляться на этапе компиляции (P1032, P1006).
На следующем заседании, которое пройдёт в США 18–23 февраля 2019 года, планируется добавить контейнерам std::string, std::vector (и возможно std::map) возможность работать на этапе компиляции.
Все добавления и правки жизненно важны для готовящейся к C++23/26 рефлексии.
Прочие мелочи
Гетерогенные поиски в unordered контейнерах
Начиная с C++20 можно будет дополнительно настраивать unordered контейнеры и обязывать их не конструировать временные объекты при операциях поиска:
struct string_hash {
// Без следующей строчки будут создаваться временные объекты!
using transparent_key_equal = std::equal_to<>;
size_t operator()(std::string_view txt) const { return std::hash<string_view>{}(txt); }
};
using unordered_set_string = std::unordered_set<std::string, string_hash, std::equal_to<> >;
template <class Value>
using unordered_map_string
= std::unordered_map<std::string, Value, string_hash, std::equal_to<> >;
// ...
unordered_map_string<int> map = { /* ... */};
assert(map.contains("This does not create a temporary std::string object :-)"));
Вещь весьма полезная, детали можно найти в документе P0919.
std::bind_front
Уже давно считается, что std::bind — достаточно опасная вещь, из-за которой легко пораниться. Поэтому в C++20 добавили более простую функцию std::bind_front.
Она не поддерживает placeholders, правильно работает с ref-qualifiers и компилируется немного быстрее. Пользоваться ей можно будет приблизительно вот так:
int foo(int arg1, std::string arg2, std::vector<int>&&, std::string_view);
// ...
auto bound = std::bind_front(foo, 42, "hello");
// ..
int result = bound(std::vector{42, 314, 15}, "word");
Всеобъемлющее описание есть в документе P0356.
std::assume_aligned
Ура, теперь можно подсказывать компилятору, что данные у нас выравнены:
void add(span<float> x, float addition) {
const auto size = x.size();
float* ax = std::assume_aligned<64>(x.data());
for (int i = 0; i < size; ++i)
ax[i] += factor;
}
Это поможет компилятору автоматически векторизовать циклы и генерировать более производительный код. Дополнительные примеры можно найти в документе P1007.
void foo(const Concept auto& value)
В P1141 приняли сокращённый синтаксис для записи шаблонных функций и классов, аргументы которых должны соответствовать концепту. Например, void sort(Sortable auto& c); значит, что sort — это шаблонная функция, и что тип переменной `c` соответствует концепту Sortable.
Микро-оптимизации
Классы std::optional и std::variant теперь обязаны иметь тривиальные деструкторы, copy/move конструкторы и copy/move операторы присваивания, если шаблонные параметры классов обладают свойствами тривиальности. Это немного поможет компилятору и стандартной библиотеке генерировать более производительный и компактный код (P0602).
Move-конструктор std::function теперь обязан быть noexcept. Если у вас есть std::vector<std::function> и конструкторы std::function раньше не были noexcept, то работа с таким вектором станет в несколько раз производительнее (P0771).
Если вы имели дело с большими массивами чисел и иcпользовали make_unique/make_shared, то иногда производительность слегка проседала за счёт того, что каждый элемент массива инициализировался нулём. Некоторые специально писали new T[x], чтобы не инициализировать каждое значение. Так вот, в C++20 добавили std::make_unique_default_init и std::make_shared_default_init. Эти две функции приехали из Boost и они не делают лишней инициализации (P1020).
Ещё добавили *_pointer_cast функции, принимающие rvalue. Это помогает избегать лишних инкрементов и декрементов атомарного счётчика при работе с std::shared_ptr (P1224).
Исправления
В великолепном документе P0608 убрали боль при использовании std::variant:
std::variant<std::string, bool> x = "abc"; // Ой! До C++20 `x` содержит `true`
Ещё один великолепный документ P0487 того же автора избавляет от граблей, на которые очень многие наступали:
char buffer[64];
std::cin >> buffer; // Теперь гарантированно не переполняется
char* p = get_some_ptr();
std::cin >> p; // Теперь просто не компилируется
Наконец, решили, что в контрактах автор класса имеет право использовать приватные члены класса в условиях контракта (P1289):
struct int_reference {
// ...
int get() const [[expects: ptr_ != nullptr ]] { return *ptr_; }
private:
int* ptr_;
};
Networking
Итак, приступим к крупным нововведениям. И начнём с плохого: в C++20 нам не видать работы с сетью из коробки. Отложили на неопределённый срок.
Modules
К хорошим новостям — подгруппа EWG одобрила дизайн модулей, так что есть все шансы увидеть их в C++20. Финальная битва за модули предстоит на следующем заседании.
Ближайшие года уйдут у разработчиков языка C++ на оптимизации компиляторов для работы с модулями и на оптимизацию представления модуля.
Так же, стоит заметить, что для идеальной скорости сборки скорее всего понадобится дорабатывать вашу кодовую базу и размечать
Ranges
Ranges в C++20 приняли. На голосовании в последний день авторы предложения P0896 сорвали долгие овации. Весь зал аплодировал стоя. Начало оваций даже успели сфотографировать, счастливый автор предложения — в шапке этого поста.
Вот пара примеров того, что можно делать с ranges:
#include <algorithm>
std::ranges::sort(some_vector);
std::ranges::find(email.c_str(), std::unreachable_sentinel, '@');
std::ranges::fill(std::counted_iterator(char_ptr, 42), std::default_sentinel, '!');
Coroutines
Возвращаемся к тому, что не приняли. Coroutines не вошли в стандарт на голосовании. Возможно, это случится на следующем заседании, но шансов маловато.
Хорошо это или плохо, подоспеет ли прототип к следующему заседанию — это открытые вопросы.
2D Graphics
Предложение о двухмерной графике воскресили, над ним продолжают работать. Автор планирует закинуть прототип в общедоступное место (например, в Boost), обкатать, собрать отзывы экспертов по 2D графике из другого комитета по стандартизации.
Заслуги РГ21
На заседании мы в основном дотаскивали stacktrace (который мы в Яндекс.Такси очень любим) до стандарта C++. Сейчас черновик документа выглядит вот так. Надеюсь, что осталось совсем чуть-чуть, и к C++20 успеем.
Ещё мы пытались привнести в стандарт плагины (динамическую загрузку библиотек, идея с stdcpp.ru). Тут нас ждал провал — предложение отклонили. Учтём ошибки и попробуем позже.
Наше старое предложение добавить атрибут [[visible]] для упрощения создания динамических библиотек, внезапно подхватил другой разработчик в документе P1283. Всячески поддерживали документ на голосованиях, первую подгруппу прошли, надеемся на успех.
Идею упростить работу с std::variant, а именно «Добавить операторы сравнения std::variant с его элементами», так же отклонили. Основные возражения — пока боязно менять std::variant, учитывая его проблемы с конструкторами (хотя после P0608) они исчезнут. Попробуем ещё раз.
С конкурентным unordered map (P0652) наоборот, всё было достаточно гладко: нам порекомендовали проверить пару альтернативных интерфейсов и сказали, что предложение почти готово для принятия в Concurrent Data Structures TS (правда, он пока только планируется).
В подгруппе SG6 Numerics мы прошлись по большинству имеющихся идей, предложили и немного обсудили механизм взаимодействия различных классов чисел (P0880). Ждём, когда начнут создавать Numbers TS, куда должны попасть все новые и вкусные классы чисел.
В подгруппе по ядру языка мы презентовали идеи о «Беспредельном copy elision», а именно P0889. Люди очень хотят нечто подобное, но не в том виде, что было изложено. Нас отправили напрямую к разработчикам компиляторов за консультацией.
Ну и, как упоминалось выше, нашу бумагу Misc constexpr bits P1032, приняли в C++20. Теперь можно будет использовать на этапе компиляции array, tuple, pair, всё что нужно для копировании std::string, back_insert_iterator, front_insert_iterator, insert_iterator.
Вместо итогов
C++20 обещает быть весьма занятным: Concepts, Contracts, Ranges, Modules, работа с временными зонами и множество constexpr нововведений.
В скором времени мы, Рабочая Группа 21, отправим комментарии к черновику стандарта C++20. Поэтому, если у вас есть какая-то боль, или вы не согласны с каким-то нововведением, пожалуйста, оставляйте свои мысли на этой странице.
Также приглашаем вас на наши ближайшие встречи по C++: Открытая встреча РГ21 в Москве и Санкт-Петербурге и C++ Siberia 2019 в Новосибирске.
Комментарии (156)
nikitaevg
28.11.2018 11:08+1Меня мучит вопрос, может кто подскажет: в бустовом variant и optional есть поддержка ссылок, в std их нет. Я читал, что это из-за неопределенности оператора присваивания. Но может стоило все-таки пойти по пути буста? Мне лично очень часто нужна опциональная ссылка, да и вариант со ссылкой тоже. Почему приняли именно такое решение, которое (кажется, сильно) ограничивает сферу примененя этих типов?
antoshkka Автор
28.11.2018 11:17+1Ссылки напрямую не поддерживаются, но всегда можно написать std::variant от std::reference_wrapper и получить поведение близкое к Boost.
А проблему вы верно указали — поведение std::varaint<int, int&> при присваиваниях будет определяться внутренним содержимым. Это показалось странноватым комитету, и решили не поощрять подобное и не переусложнять класс variant. Те кому всё же нужно подобное поведение всегда могут воспользоваться reference_wrapper
gbug
28.11.2018 12:09+1Может кто-нибудь разъяснить что значит dynamic_cast внутри constexpr-выражения? Как это вообще понимать?
VioletGiraffe
28.11.2018 12:18+1А в чём проблема? Известен тип и значение аргумента, можно вычислить результат. Если такое приведение невозможно, результатом будет nullptr.
gbug
28.11.2018 14:29+1Тогда получается, что это просто static_cast, который возвращает nullptr в случае неудачи, так?
antoshkka Автор
28.11.2018 14:31+1Да. Ну и вам будет проще переносить имеющийся код на constexpr — dynamic_cast будет просто работать, не надо будет их заменять на странные конструкции с std::is_constant_evaluated.
gbug
28.11.2018 14:39+1Фух, понял! Благодарствую, а то с утра не отпускало:)
(Плюсануть не могу, неполноправный.)
antoshkka Автор
28.11.2018 12:22Если мы создаем переменную в constexpr контексте, то компилятор об этой переменной должен всё знать, включая её внутреннее представление и базовые классы.
И соответственно, раз компилятор о ней всё знает, то он может и отслеживать тип переменной, даже если она передаётся по ссылкам на базовый класс. Ну а раз так, то почему бы не разрешить ему и dynamic_cast делать:
struct base { constexpr virtual ~base() = default; }; struct forty_two: public base { constexpr int say_42() const { return 42; } }; constexpr int try_say(const base& b) { forty_two* p = dynamic_cast<forty_two*>(&base); if (p) { return p->say_42(); } return 0; } constexpr void do_something() { constexpr forty_two ft{}; static_assert(try_say(ft) == 42); }
gbug
28.11.2018 14:18+1Ага, понятнее, спасибо! А в случае, если переменная создана не в constexpr-контексте, тогда и функция перестанет быть constexpr, так?
antoshkka Автор
28.11.2018 14:29Да, всё верно. Функция будет выполняться на рантайме. Или будет ошибка компиляции если попробуете использовать эту функция в том месте, где компилятор требует вычисление на этапе компиляции:
void do_something() { /*constexpr*/ forty_two ft{}; // Следующая строчка не соберётся. Компилятор будет жаловаться, // что переменная `ft` не известна на этапе компиляции и он не может // выполнить `try_say` как constexpr функцию. static_assert(try_say(ft) == 42); }
NeoCode
28.11.2018 13:03+1По сути все идет к тому что в каждом компиляторе C++ будет встроенный интерпретатор C++. Интересный путь развития…
antoshkka Автор
28.11.2018 13:07Интересно было бы ещё дать пользователям доступ к этому интерпретатору на рантайме. Чтобы можно было пользовательский ввод интерпретировать как функцию, компилировать и выполнять её.
rkfg
28.11.2018 14:39Это ведь достаточно редко нужно, а так clang, вроде, достаточно модульный, чтобы его библиотеки использовать в своём коде.
eao197
28.11.2018 16:05+1Можно посмотреть на механизм текстовых mixin-ов из D. Там можно вызвать CTFE функцию (compile-time function evaluation, аналог плюсовых constexpr/consteval), которая возвращает строку. Строка скармливается конструкции mixin. Компилятор берет эту строку как подлежащий разбору и обработке исходный код.
У такого подхода есть большой плюс. Функции, которые генерят строки для mixin-а, — это обычные функции и их можно обычным образом отлаживать и покрывать тестами. Поэтому написать генератор кода для какого-то своего DSL не сложно.
Но есть и проблема, как говорят D-шники: если в mixin отдали сгенерированный в compile-time текстовый фрагмент, например, такого вида:
а потом в программе написали:class Generated { void f() {...} void g() {...} }
auto obj = new Generated; obj.g();
то войти отладчиком внутрь Generated.d() пока нельзя. Но это особенность текущей реализации D-шных компиляторов, они не генерируют отладочную информацию для таких случаев.
В C++ часть того, что можно делать на текстовых mixin-ах из D, может стать доступной через метаклассы. Но, наверное, не все.
alexgpg
28.11.2018 13:58+1А почему Networking отложили?
antoshkka Автор
28.11.2018 14:02Возникли подозрения, что дизайн можно улучшить, используя последние новинки C++20 (в основном концепты, и немного ranges).
mapron
28.11.2018 19:55Так автор Networking вроде на cppcon об этом прямо говорил, что «ребейзить» надо.
antoshkka Автор
28.11.2018 20:00О! А можно ссылочку на видео, а то я найти не могу :(
mapron
28.11.2018 20:09www.youtube.com/watch?v=hdRpCo94_C4 (да, это не автор TS)
вот это вроде, долго чет искал) Таймкод с пруфом про «надо ребейзить» не подскажу. Если я ошибся, то может и не это видео вовсе. Извините в случае чего.
eao197
28.11.2018 14:43А про алгебраические типы данных и паттерн-матчинг что-нибудь известно? Вроде как перед этой встречей появилось несколько конкурирующих предложений на эту тему.
antoshkka Автор
28.11.2018 15:23До их рассмотрения дело не дошло. Зато успели немного пообсуждать монадические интерфейсы для std::optional.
eao197
28.11.2018 15:27До их рассмотрения дело не дошло.
Жаль. Видимо, придется ждать C++23 или даже C++26.
Зато успели немного пообсуждать монадические интерфейсы для std::optional.
Дурное дело не хитрое.
Шутка :)
nikitaevg
28.11.2018 17:38Можете вкратце описать результаты обсуждения? Было бы очень интересно увидеть когда-нибудь что-то такое.
0xd34df00d
28.11.2018 21:06Зачем только для
std::optional
, если можно просто корутины доработать и выразить через них (монады для конкретноoptional
у меня уже есть, а вот дляEither
я не осилил сделать, там байнд для левого случая нетривиальный).
Antervis
28.11.2018 15:46а можно поподробнее про
// Следующие две строчки могут пагубно повлиять на рассудок: assert(c_auto[4] == 11); // не константно assert(c_auto[5] == 11); // константно
из примера про is_constant_evaluated? Почему так?
Может ли к с++20 constexpr приехать в математические функции стандартной библиотеки? Просто глупо получается, что после всех притянутых в стандарт constexpr-фич попытка буквально «что-нибудь посчитать на этапе компиляции» спотыкается об какой-нибудь std::sinZaMaZaN4iK
28.11.2018 15:57antoshkka Автор
28.11.2018 16:21Математические функции уже почти сделали constexpr, я надеюсь что успеется к C++20. LEWG одобрила бумагу https://wg21.link/P0533, осталось пройти всего одну группу.
Основная проблема — это имплементация. У GCC всё хорошо в этом месте, а вот Clang не может воспользоваться библиотекой от GCC для математических вычислений на этапе компиляции — лицензия не позволяет. А написать подобную библиотеку самим — это огромные трудозатраты (приблизительно сотня или тысяча человеколет).
Так что когда примут в стандарт, некоторые компиляторы часть функций не смогут сделать constexpr в течение длительного времени.
antoshkka Автор
28.11.2018 16:11Про странный пример… Всё сводится к вычислению вот этих двух строчек с переменной `in` известной только на рантайме:
const int v_runtime0 = __builtin_is_constant_evaluated() ? in : 11; const int v_runtime1 = __builtin_is_constant_evaluated() ? 11 : in;
Так вот, если для v_runtime0 функция is_constant_evaluated вернёт true, то получится что v_runtime0 надо подсчитать константно с помощью рантайм переменной. Это что-то невозможное, поэтому тут возвращается false.
Для v_runtime1 если функция is_constant_evaluated вернёт true, то никаких противоречий нет и можно подсчитать константно.
Как справедливо заметили в чатике t.me/ProCxx, решать надо как в загадках про лжеца: «могло быть в первом ответ y? нет, потому что было бы противоречие».
mkshma
28.11.2018 16:19+1К C++20 многие (и я в их числе) хотят увидеть в стандарте контейнеры, которыми можно пользоваться на этапе компиляции.
А можно юзкейсов немного? А то я пока даже в принципе не понимаю зачем нужны constexpr. constexpr контейнеры же для меня вообще rocket-science какой-то.antoshkka Автор
28.11.2018 16:45Самый ожидаемый мной контейнер — constexpr std::string. За счёт того, что компилятор сможет создавать такую строчку на этапе компиляции, иницилизация в следующем коде перестанет быть динамической и станет статической:
void foo() { static const std::string date_time_fmt = "DATE: YYYY MM DD TIME: BLA-BLA"; }
Другими словами, вся инициализация будет происходить на этапе компиляции, и при первом заходе в функцию не будет залочиваться мьютекс (или ещё как-то потоко-безопасно инициализироваться статическая переменная), не будет динамически выделяться память…
Весь код с статическими/глобальными строковыми переменными станет работать немного быстрее.
Другой практический пример — это constexpr std::unordered_map. Можно будет делать подобие switch по строкам.
Любителям метапрограммирования с constexpr std::vector откроются новые возможности. Так например подобное нечитаемое безобразие просто не надо будет писать, так как можно будет обойтись std::vector и алгоритмами из стандартной библиотеки. Метапрограмный код получится читаемый, а не тот что мы имеем в C++14/17.
Ну и наконец — без constexpr контейнеров не сделать рефлексию, а её очень хотят успеть к C++23.ABBAPOH
28.11.2018 18:49Любителям метапрограммирования с constexpr std::vector откроются новые возможности. Так например подобное нечитаемое безобразие просто не надо будет писать, так как можно будет обойтись std::vector и алгоритмами из стандартной библиотеки. Метапрограмный код получится читаемый, а не тот что мы имеем в C++14/17.
Не является ли данный пример типичным местом применения для gsl::span?
Кстати, как дела у std::array_view, всё заглохло?
constexpr строки и вектора это оч хорошо (вью же не сложишь), но на моей практике многое делается уже c одним gsl::span (хотя я знаю у себя место где constexpr вектор бы упростил код)
Upd: а, там массив возвращается; ну да, вектор полезен был бы
bibmaster
29.11.2018 09:45А чем не вариант для константных строк использовать string_view? Если она инициализируется литералом, время жизни обеспечено. Можно даже отнаследовать какой нибудь literal_view с constexpr конструктором из массива char.
antoshkka Автор
29.11.2018 10:17Для константных — самое то. Но вот выше люди коментируют, что хотят макросами имена функций и переменных генерировать. Для того чтобы это можно было делать через рефлексию, а не макросы, нужен constexpr string.
ABBAPOH
28.11.2018 19:23constexpr нужны для манипуляциями над данными в RO памяти, которых в программах может быть достаточно много. Например, у нас есть массив, данные в котором мы ищем по индексу. Как не нафакапить при заполнении массива и не промахнуться с индексом? Очень просто, написать constexpr функцию, проверяющую валидность массива и вызвать её из static_assert.
А теперь допустим мы ходим искать в этом массиве не по индексу, а по содержимому.
Сделаем constexpr хэшмапу и будем искать за (примерно) константное время. Впрочем, этот велосипед был скорее на поиграться с возможностями нового с++; в мире розовых поней я лучше подожду constexpr std::unordered_map.
sborisov
28.11.2018 16:30Всё очень круто! Но будет ли помещаться С++20 в голову разработчику?
Нужен новый Майерс, раз уж старый Майерс завязал с объяснениями хитростей С++!ZaMaZaN4iK
28.11.2018 16:36Для этого даже создали отдельную подгруппу в комитете :-) Если у Вас есть идеи, как можно помочь — то добро пожаловать :)
antoshkka Автор
28.11.2018 18:49+2Но будет ли помещаться С++20 в голову разработчику?
Большинство стандартных библиотек современных языков программирования не помещаются в голову разработчику: Java, C#, C++, Python…
Я немного не следил за событиями: в других языках тоже жалуются?
Amomum
28.11.2018 19:08А как поживает constexpr с восклицательным знаком?
domix32
28.11.2018 19:13+1Это же упомянутый consteval? Или что-то ещё?
Amomum
28.11.2018 19:17+2Да, проморгал. Спасибо!
Слава богу, решили выпилить этот восклицательный знак, фуф.mapron
28.11.2018 19:58Ну там и доллар из метаклассов и рефлексии выпилили.
Amomum
28.11.2018 21:33И это проморгал :( Ссылку можно?
mapron
28.11.2018 22:34По метаклассам вот последняя ревизия:
www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0707r3.pdf
по рефлексии
www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0194r6.html
изменение с 8 по 9 ревизии посмотритеAmomum
28.11.2018 22:43Спасибо!
old: $class M new: constexpr void M(meta::type target, const meta::type source)
Ммда, ну, поживем-увидим.mapron
28.11.2018 23:10Это еще что. Там на проперти вообще ужас какой-то. (то что в целом метаклассы превращаются в эдакие функции, я не вижу плохого, но надеюсь синтаксис еще доработают. Саттер тоже не в восторге).
mapron
28.11.2018 20:01+2Интересно было бы услышать тех, кто 2D графику в опросе выбрал — как вы будете это использовать, для чего? Использовать стандартную библиотеку без какого-либо графического фреймворка?
antoshkka Автор
28.11.2018 20:25Рисовать таблицы, графики, обрабатывать изображения, простейший GUI (да, без фреймворка). Лично мне сильно не хватает изкоробочной возможности отрисовать картинку со статистикой и отдать её пользователю как веб страницу.
mapron
28.11.2018 20:40Вы мимо, но спасибо)
отрисовать картинку со статистикой и отдать её пользователю
Ну пользователю-то картинку и пожать придется? я почитал
www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0267r7.pdf
там же рендеринг только.
Не будут же туда еще видео кодеки пихать)antoshkka Автор
28.11.2018 21:45Там есть сохранение в формате изображений (PNG и JPEG точно были).
mapron
28.11.2018 22:29Да, я виноват, просмотрел, 13.9 раздел, если кому интересно.
В такой постановке, согласен, может быть полезно для минималистичного взаимодействия. Все равно меня немного напрягает «внешняя зависимость» в виде PNG кодеков в стандартной библиотеке, и как это все будет специфицировано (с учетом того что периодически всплывают уязвимости в libpng той же).antoshkka Автор
28.11.2018 22:43В юникоде, стандартной библиотеке и demanglingе тоже уязвимости всплавают. Пока всё норм.
domix32
28.11.2018 20:41+2то есть подтянуть внутрь стандартной библиотеки libpng/libbmp/libsvg? Или в каком виде отрисовывать? Почему не скомпилировать это wasm и рисовать в канвас из локального фреймбуффера если это веб?
ABBAPOH
28.11.2018 21:00То есть Cairo в стандарте не смущает (а это и есть пропозал), а libpng смущает?)
snizovtsev
29.11.2018 00:08+2Смущает. Все серьезные приложения (вроде браузеров) уже переехали на Skia, а тут стандартизацию Cairo ещё только обсуждают.
domix32
29.11.2018 01:49Смущает. Хотя наверное было бы неплохо иметь некоторый кроссплатформенный интерфейс для кодеров/декодеров изображений и аудио до кучи. Но это не раст с его типажами, поэтому довольно сомнительно все это.
DistortNeo
28.11.2018 20:50+2А зачем это в стандарной библиотеке?
antoshkka Автор
28.11.2018 21:47-1Чтобы можно было пользоваться, а не страдать. Networking, std::thread, Filesystem служат той же цели (впрочем как и вся стандартная библиотека).
DistortNeo
28.11.2018 21:55+2С++ нужна не раздутая стандартная библиотека, а нормальный репозиторий для всего этого хозяйства.
antoshkka Автор
28.11.2018 22:26C++ в первую очередь нужна функциональная стандартная библиотека.
mapron
28.11.2018 22:31Согласен, но может это хотя бы сделают на откуп вендорам, как было со стандартизацией больших интов и GC? просто например какой-нибудь вендор под embedded может и не захотеть реализовать стандарт под свою железку. (хотя чего я, как будто бы сегодня их заставляют реализовывать весь стандарт).
antoshkka Автор
28.11.2018 22:41Сейчас наоборот идёт движение в сторону того, чтобы явно разметить в стандартной библиотеке embedded/freestanding части. Тоесть выделить некое подмножество стандартной библиотеки, которое работает на любой платформе, даже на такой где нет оперативной памяти, файловой системы и проч.
mapron
28.11.2018 23:12Ееее! Это очень крутое движение, рад был про это слышать (удивительно как я такое пропустил).
snizovtsev
29.11.2018 00:06+2Networking, std::thread, Filesystem
Это всё следствия бетонирования POSIX. А графический стек везде разный и меняется постоянно, чтобы соответвовать новым ускорителям и железкам. Ваша библиотека морально устареет раньше, чем её успеют стандартизировать.
antoshkka Автор
29.11.2018 10:27Я не специалист в 2D графике, но насколько мне известно основные принципы отрисовки за последние 30 лет не менялись. Применяешь к холсту примитивы, говоришь отрисовать, готово.
Поправьте меня если я не прав.
Amomum
28.11.2018 22:46+2А как поживает P0631R0? Появится ли наконец-то в С++20 стандартная константа для числа Пи? Или так и будем 4*atan(1)?
mapron
28.11.2018 23:32Что-то мне не очень понятна ситуация c [[visible]]:
www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0276r0.html
и
www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1283r0.html
Пусть у нас в хедер файле
[[shared]] void library_foo();
Когда мы его подключаем из реализации, считается, что мы делаем экспорт.
Когда включаем это объявление, и реализации нет — считаем что импортируем из библиотеки.
Теперь вопрос, а если у нас этот же хедер используется для статической библиотеки? тут уже без макроса не обойтись, верно? Иначе использующий API код не сможет определить, что ему там вставлять, обычную слабую ссылку или импорт (в MSVC там будут отличаться имена символов ж).Antervis
29.11.2018 03:23отдельные аттрибуты для импорта/экспорта — чисто msvc-шная проблема и если они её решат, то лучше будет всем.
mapron
29.11.2018 12:04Дело не в отдельный аттрибутах (я про них не упоминал). Уверен, разработчики смогут справиться. Речь о том что разные имена в ABI, и это не покрыть фичами языка (либо создавать новый ABI которым никто не будет пользоваться).
vanxant
29.11.2018 02:17Язык без «изкоробочной» поддержки UTF-8 в 2020 году это провал, ребята.
Если комитет этого не понимает, то анонсированного выше в комментариях С++35 просто не будет.mapron
29.11.2018 13:17C++ это такой язык, что даже не будь изкоробочной поддержки в стандартной библиотеке чего угодно, это бы не привело его к провалу.
Существующая практика такова, что почти всегда для реализации нужных функций нужны сторонние библиотеки.
Посмотрите на Boost.Text, может вам понравится:
www.youtube.com/watch?v=944GjKxwMBo
p.s. я даже не буду комментировать «провал» языка который живет 30 лет и загибаться не планирует.vanxant
29.11.2018 17:14+1Чувствую себя агрессивным проповедником в чужой церкви, но блин, коллеги, давайте смотреть правде в лицо.
Что у нас было 20 лет назад? Был STL, которым никто не пользовался, даже самыми базовыми вещами. Все юзали те же строки и массивы или в стиле С, или CString/CArray, или свои велосипеды. При этом CString уже поддерживал (тогдашний) юникод. Однако С++ являлся топовым универсальным языком, на котором писали всё — от системных утилит до фронтенда.
Прошло 20 лет, строки С++ всё ещё не поддерживают юникод.
Ну ок, фронтенд потерян безвозвратно. Весь бизнес-приклад давно переехал на java/.net. Как и 90% кода операционных систем. Бэкенд, всякие веб-сервисы, отжали пых и руби. Новые системные тулзы пишут на расте или го, не на плюсах. Наука со своим матаном переехала на питон. Геймдев ещё держится, но с этими вашими смартфонами и там уже не всё гладко.
Может С++ загибаться и не планирует, но по факту он уже сегодня воспринимается ближе к фортрану или коболу, чем к промышленному языку.
Простите, не хотел никого обидеть.antoshkka Автор
29.11.2018 17:31+4Хм… я пишу это сообщение из браузера написанного на C++, используя операционку у которой пользовательские библиотеки почти все на C++, собранную компилятором написанном на C++… разработчики которого играют в игры, написанные на C++ :)
… при этом работаю в фирме, где «бизнес приклад» написан на C++, и даже веб сервисы написаны на C++.
За всю науку говорить не могу, но в CERN бозон хиггса искали используя плюсы (и Boost), в ИММ РАН физические задачи решают на C/C++/Fortran… да и в МГУ тоже физики используют C++.
Что-то в ваших показаниях не сходится.
eao197
29.11.2018 17:51Был STL, которым никто не пользовался, даже самыми базовыми вещами.
Вы вот как-то с ходу начали оперировать ошибочными постулатами. Я, например, один из тех, кто начал использовать STL еще до принятия C++98. Тогда по рукам ходила реализация STL от Rogue Wave, кажется, которую можно было скотчем и спичками прицепить к Watcom C++, Borland C++. А в 1998-ом в Visual Studio 6.0 STL уже был из коробки, хотя и не стандартный еще.
Так что вы уже со своим тезисом «никто не пользовался» не правы. Ну и дальше, если копнуть, везде такая же история.vanxant
29.11.2018 18:05Ребят, да я про тенденцию говорю. Тогда на плюсах писали реально всё, из конкурентов был или чистый Си или Паскаль/Дельфи.
Сейчас мы ниши перечисляем. Вот мол у автора статьи ОСь и браузер написаны на плюсах. А у меня ось на джаве, а браузер потихоньку на раст переписывают.
Если бы лямбды, auto и нормальный for появились на 10 лет раньше, этого бы не было.eao197
29.11.2018 18:14А у меня ось на джаве
Это какая?
Если бы лямбды, auto и нормальный for появились на 10 лет раньше, этого бы не было.
Да ладно, устойчивая тенденция миграции с C++ на Java началась где-то в 2000-ом или 2001-ом. К 2004-ому она уже была вполне себе четкой. И Java тогдашняя по выразительности с C++98 не могла соревноваться.
Зато она была безопасной, со сборкой мусора, большим JDK. И даже умудрялась не так уж и сильно тормозить на тогдашней технике.
За прошедшие 15 лет C++ уже вытеснили из тех ниш, где его выгодно было заменить на Java, C#, Scala и пр. Но там, где он остался и применяется сейчас только Rust и, отчасти, Go может что-то предложить. Да и тому же Rust-у еще экосистему свою вырастить нужно. А то что ни растоман, то и жалоба, что Qt у них нет.vanxant
29.11.2018 18:38Это какая?
Ведроид жи ну. Да-да, я знаю, что там внутре у ней неонка, в смысле линукс, но он тоже не на плюсах написан.antoshkka Автор
29.11.2018 18:41+2У Андроида практически весь userspace написан на C++, включая линкер.
eao197
29.11.2018 19:34Ну так не на Java же OS написана. Пока. Вроде как Google хочет со временем на Фуксию перевести, которая уже на плюсах пишется.
vanxant
29.11.2018 19:41+1ОС это не только и не столько ядро. Это библиотеки, утилиты, планировщики, рюшечки, окошечки и прочие пакетные менеджеры. Посмотрите на зоопарк дистрибутивов Линукса — перевести прод с условной федоры на убунту не сильно проще, чем с винды (из-за чего всякие докеры и появились).
Возврат гугла к плюсам обусловлен, в первую очередь, тем, что у самой java проблем выше крыши, причём как технических, так и юридических и организационных. Ну и да, здесь надо похвалить комитет, плюсы сейчас развиваются действительно поживее, чем java.eao197
29.11.2018 19:44Это библиотеки, утилиты, планировщики
И вот это все на C или C++.
рюшечки, окошечки и прочие пакетные менеджеры
А это уже относится к ОС, если под ОС понимать что-то вроде Windows. А вот в том же Linux-е есть сама ОС и есть Desktop Environment. Вот окошечки-рюшечки — это про Desktop Environment, а не про ОС.
antoshkka Автор
29.11.2018 18:45Слышал что уже длительное время идёт обратная миграция. Процессоры почти перестали расти в тактовой частоте и в основном растут в количестве ядер. Ну и тут grabage collector начинает играть злую шутку: если у вас 1 ядро, и GC отрабатывает за 1мс, то вы теряете 1мс процессорного времени; если у вас 16 ядер, и GC отрабатывает за 1мс, то вы теряете 16мс процессорного времени. Альтернативные подходы к GC обладают аналогичными недостатками.
eao197
29.11.2018 19:36Слышал что уже длительное время идёт обратная миграция.
Да тут сложно оценки сделать. С одной стороны — да, есть прецеденты, когда с Java переходят на C++ (или используют в Java код на плюсах через JNI). С другой стороны, на Java и C# делают тот же самый HFT, где плюсам самое место (и где они хорошо себя чувствуют).domix32
29.11.2018 23:37А можно ткнуть пальцем, где HFT на шарпах/java пишут? Я пониаю когда околовычислительне всякие микросервисы, графики рисовать, отчеты пилить, но вот чтобы прям непосрдественно общение трейдеров — не сышал.
eao197
29.11.2018 23:58Наверное, самый известный — это Lmax со своим Disruptor-ом.
Несколько лет назад здесь, на Хабре было интервью с Романом Елизаровым, он там рассказывал про разработки трейдинговой системы на Java. Там же есть ссылка на OpenHFT.
Про разработку на C# в обсуждении на RSDN-е кто-то рассказывал (отсюда и далее вниз по ветке).
Antervis
29.11.2018 18:58+1давайте перефразируем: раньше выбора не было, а сейчас он появился. Раньше железо не позволяло лишний раз писать на джаве/диезе/питоне, а сейчас позволяет. То, что появилась достойная альтернатива в виде раста — хорошо, но едва ли в ближайшем будущем все забросят свои кодобазы и сбегут на него. У плюсов есть старые детские проблемы, и это не значит что их не надо (или нельзя) поправить
vanxant
29.11.2018 19:20Понимаете, если комитет до сих пор спорит, не пора ли в языке сделать, наконец, строки с поддержкой юникода, то конкурировать такой язык будет, как заметил antoshkka, разве что с фортраном за умы седовласых академиков.
antoshkka Автор
29.11.2018 19:24+1Комитет не спорит на тему «надо ли», комитет обсуждает как это сделать правильно. Если вы знаете как это делать правильно — то пишите proposal, вас с радостью выслушают.
P.S.: А передёргивать слова некрасиво, я такого не говорил.vanxant
29.11.2018 19:28-1(Пример про ИММ РАН разве не вы предложили?)
antoshkka Автор
29.11.2018 20:20+1Примеры передёргивания:
1) Java иногда используются для студенческих курсовых. Жаль, ведь получается что этот язык — лишь удел недоучек.
2) Андроид использует Java. Не спроста vanxant утверждает что данная ОС — удел разработчиков недоучек.
Тут приличное общество, давайте не будем опускаться в дискуссиях до подобных приёмов.
Antervis
29.11.2018 19:25предложите навскидку zero-overhead юникод библиотеку
vanxant
29.11.2018 19:35+1А нету таких. Переход на юникод это боль и страдания. Питон страдал несколько лет, php до сих пор страдает с mbstring и func_overload, и так далее. Но вариантов нет, мир уже чуть больше, чем горстка юзеров в англоязычных странах.
Комитет на то и нужен, чтобы изучить ошибки других, сжать тестикулы в кулак, принять решение и добровольно-принудительно перейти всем сообществом. А не как у нас в плюсах принято, что в языке несколько видов строк, несколько видов «умных» указателей, а к четырём одновременно существующим видам обработки ошибок предлагается добавить ещё столько же. Берёшь либу и не знаешь, что от неё ожидать.
Есть, в конце концов, буст, где можно обкатать потенциальных кандидатов «в живой природе» до их стандартизации.domix32
29.11.2018 23:39А можно ссылку на историю о страданиях питона? Я думал его изначально с поддержкой делали.
mapron
30.11.2018 00:25«Берёшь либу и не знаешь, что от неё ожидать»
Пока для меня, основная боль как разработчика, это не то что в С++ нет какой-то там поддержки utf-8, а то что сторонние кроссплатформенные C библиотеки просто используют fopen/stat и т.п. которые разумеется на Win работают, но без поддержки юникода.
— ffmpeg/vidstab
— opencv — весь файловый api
— glog
— dlib
(первое что за минуту в голову пришло, тысячи их)
Да, патчи-то наложить недолго, но е-ёмое, не понимаю я разработчиков библиотек, под винду эти функции существуют два десятка лет, вы делаете «кросплатформенность» и забиваете на поддержку юникода в FS? что с вами не так?
MS, сделайте уже поддержку setlocale(«utf-8»), чтобы fopen работал как везде, это не лечится)
Panzerschrek
29.11.2018 08:39+2Раньше у нас были char, signed char, unsigned char, wchar_t, int8_t, uint8_t, теперь ещё char8_t появился. Как-то уж слишком много сущностей, полоснуть бы бритвой Оккама по ним.
Anton3
29.11.2018 21:25char = текст ASCII
chat8_t = code unit из UTF-8
int8_t, uint8_t = целые числа
std::byte = неизвестные данные (не текст и не числа)
wchar_t = системный тип символа (привет, ужасы кодировок Windows)
signed char, unsigned char = что угодно в устаревшем коде; стоило бы выпилить
dm_frox
29.11.2018 12:57Интересно, когда все эти радости начнут поддерживать популярные компиляторы. Судя по поддержке С++11/14/17 как раз где-то к 2023 году. А до этого останется только обсуждать все это в блогах. И ведь еще дожить надо.
mapron
29.11.2018 13:01+2Вы неправы. Начиная с С++14, поддержка максимум на год задерживалась. Другое дело, что если концепты вы можете начать использовать сразу после появления их поддержки в тулчейне, то вот с модулями не все так просто) минимум ждать поддержки и в системах сборки, а максимум — во всех используемых вами библиотеках.
Ну вот сейчас 2018 год, С++17 полностью (по крайней мере на словах) 3 популярных компилятора поддерживают. У нас все проекты собираются с флагами 17 стандарта.
Благодаря регулярным и предсказуемым релизам, разработчики компиляторов могут как раз быстрее реализовывать фичи. Т.е. в 2019 заморозят набор фич, и к 20 году большая часть в том или ином виде будет реализована со спец флагами в компиляторах. Для концептов, контрактов и модулей, кстати, флажки или экспериментальные ветки уже есть.RamzesXI
29.11.2018 15:50С++17 полностью (по крайней мере на словах) 3 популярных компилятора поддерживают.
Как там поживает <memory_resource>? А < execution >?
mapron
29.11.2018 16:11А вот тут речь уже о том что фичи стандартной библиотки тянутся медленнее)
Компилятор — «да! да здравствует новый стандарт!», его реализация STL — «подождите, пожалуйста помедленнее, я запсваю» =)
Если не брать штуки по constexpr — то большая часть мажорных нововведений как раз по части компилятора, я думаю с ними проблем не возникнет.
в любом случае, согласитесь, что 5% отдельных фич, с которыми проблемы, это не та вещь про которую можно ныть «ой беда, 10 лет теперь стандарт реализовывать будут».
exception13x
Есть новости, когда планируется отказаться от хэдеров и препроцессора?
antoshkka Автор
Нужно чтобы все проекты переехали на модули, тогда и можно будет отказываться (другими словами — не раньше C++35)
babylon
Сделали бы нативный аллокатор в нотации MessagePack. На модули перейдут неизбежно.
antoshkka Автор
Расскажите поподробнее про аллокатор. Чем он хорош?
babylon
Его в природе не существует… MessagePack предназначен для упаковки объекта, но многого с этим упакованным объектом поделать нельзя. Вот если бы сделали аллокатор то можно было дампить напрямую, модифицировать, разбирать и собирать на эндах, "шардить", парсить в AST и т.д. Всё как мы любим. Я за себя говорю. Я бы нашел ему применение это точно.
VioletGiraffe
Без макросов некоторые вещи не делаются, когда будут делаться, тогда и можно отказываться. Вещи, связанные с удобным для программиста генерированием повторяющегося исходного кода.
kovserg
Интересно, а идей улучшить препроцессор вместо того что бы хаить макросы не возникало?
antoshkka Автор
А как именно вы предлагаете его улучшить?
Gorthauer87
Ну можно например внести гигиенические макросы аля Rust, хоть они тоже не идеальные, но намного мощнее и при этом безопаснее.
antoshkka Автор
А расскажите поподробнее, как вы видите подобные макросы в C++?
Gorthauer87
https://doc.rust-lang.org/book/first-edition/macros.html
Antervis
вы пропустили важную деталь:
т.е. без конфликтующего синтаксиса, breaking changes и чтобы был прок
Gorthauer87
Мне казалось, что функции с восклицательным знаком запрещены в плюсах. Соответственно их никто не может писать, а если на них сделать макросы, то нигде совместимость не сломается
Antervis
ок, сделать допустим можно. Нужно ли? Для большинства случаев шаблонов более чем достаточно, а с появлением концептов не хуже ни синтаксис, ни гарантии. То, что на шаблонах сейчас сделать сложно или нельзя, будет рефлексия. Для случаев, связанных с условной компиляцией (дебажная информация, юнит-тесты и пр.) эти гигиенические макросы всё равно будут полагаться на сырые макросы флагов компиляции. Для остального (документация, например) требуется не столько поддержка от компилятора, сколько от системы сборки.
mapron
То что я выше посмотрел по ссылке, не рефлексию больше напоминает, а generator expressions, которые позволяют ast менять, очень для метаклассов будут полезны. Но это точно не C++23, как Саттер сказал. Может быть какой-то TS к 26 году разве что.
IRainman
Это всё опять же «такое себе решение».Нужно элементарное: больше базового синтаксиса на этапе компиляции тогда и макросы станут не нужны.
Вот, например, я хочу написать один шаблон с большим количеством кода, но обрабатывающий либо 1 элемент без списка либо множество элементов из списка и функция обхода элементов списка сама по себе сложная. Так вот макросом или внутренними директивами компилятора я могу не плодить адовую копипасту, а вот на C++ даже с шаблонами и последним стандартом до сих пор не могу, точнее могу могу но костыли, вроде, sfinae не выглядят решением ибо это костыли, ужасные костыли, а я хочу писать, примерно так:
Всё упирается в элементарное if этапа компиляции которое могло бы выключить кусок функции если у нас в классе есть метод для получения одного элемента и нет метода для получения коллекции.
P.S. в идеале хотелось бы даже шаблон не создавать, но это уже совсем мечты.
antoshkka Автор
Так уже можно:
DistortNeo
Можете привести пример кода, который невозможно сгенерировать с помощью шаблонов и constexpr?
eao197
Регулярное выражение?
DistortNeo
А как их можно макросами реализовать?
А вот constexpr — пожалуйста:
github.com/hanickadot/compile-time-regular-expressions
eao197
Так затем в рантайме все равно приходится работать со string-view и формировать из результатов разбора нужные значения.
Вот на продвинутых макросах (или на D-шных CTFE+mixin), гипотетически, можно получить что-то вроде:
где year будет иметь тип int, а month и day — тип byte.
domix32
Какой-нибудь кейс когда проще написать макрос нежели шаблон/функцию для регулярки?
Amomum
Конкатенация строк в названиях переменных?
DistortNeo
Не вижу в этом ничего хорошего. Код пишется один раз, зато читается постоянно. Упрощение написания кода ценой усложнения его чтения и отладки — это выстрел в ногу.
Amomum
Вы просили пример — я его привел :) Я не говорю, что это хорошо, но на шаблонах и лямбдах так не сделать.
0xd34df00d
Сегодня или с рефлексией?
Если сегодня, то
BOOST_FUSION_ADAPT_STRUCT
и подобные, например.Adler3D
DistortNeo
Спасибо, хороший пример, что отсутствие рефлексии в C++ — это плохо. Но альтернатива с variadic templates и string literals таки возможна:
habr.com/post/243581
svr_91
А какие-нибудь замены макросам __FILE__, __LINE__ уже придумали?
antoshkka Автор
Да, std::source_location
DistortNeo
От препроцессора отказаться невозможно в принципе, т.к. без условной компиляции не обойтись. Но я бы хотел, чтобы действие директив препроцессора ограничивалось только одним файлом. Именно файлом, а не translation unit.
Punk_Joker
А constexpr (в С++17) разве не позволяет организовывать условную компиляцию?
antoshkka Автор
`if constexpr` позволяет
0xd34df00d
if constexpr
, увы, не позволит сделать что-то вродеДа и без выкрутасов что-то вроде
не сделаешь.
DistortNeo
Нет, не позволяет — обе ветки должны компилироваться.
0xd34df00d
Нет, там всё сложнее. Они не обязаны тайпчекаться, по крайней мере, в тех контекстах, которые зависят от шаблонных параметров.
vanxant
Вот не надо вот от препроцессора отказываться. Это ещё более замедлит принятие С++ на всяких микроконтроллерах и SoC, где до сих пор пишут на С образца семьдесят лохматого года.
Antervis
пишущие на си образца семидесятых годов заведомо не аудитория новых стандартов с++
exception13x
Знакомые программисты микроконтроллеров говорили, что не используют плюсы из-за слишком толстой стандартной либы.
Amr
Это заблуждение или как говорят, ваши коллеги скорее всего не умеют готовить embedded проекты на С++.
Стандартная либа в таких проектах обычно не используется, а вот все плюшки языка доступны
antoshkka Автор
Рекомендую показать вашим знакомым consteval-функции C++.
В комитете присутствующие embedded разработчики были вне себя от радости. Теперь можно легко заставлять компилятор вычислять что-то только на этапе компиляции, без попадания этого в рантайм.