Концепты вместо страшного SFINAE в C++ 20!
Раньше для ограничения шаблонов по типу приходилось писать SFINAE через
Сначала посмотрим на «старый» подход с
Здесь логика «только для целочисленных типов» зашита в возвращаемый тип, сигнатура читается тяжело, а сообщение компилятора при ошибке — длинное и мало что объясняет.
Теперь опишем концепт
Используем этот концепт в сигнатуре функции через
И напишем простой
🔥 Компилятор теперь выдаёт понятное сообщение: «ограничение
📣 C++ Ready | #практика
Раньше для ограничения шаблонов по типу приходилось писать SFINAE через
std::enable_if и читать страшные сообщения компилятора. В C++20 появились concept и requires, которые позволяют описать те же ограничения гораздо проще и понятнее.Сначала посмотрим на «старый» подход с
std::enable_if, где функция должна работать только для целочисленных типов:template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
sum(T a, T b) {
return a + b;
}
int main() {
auto x = sum(1, 2); // ок
auto y = sum(1.0, 2.0); // ошибка компиляции с SFINAE
}
Здесь логика «только для целочисленных типов» зашита в возвращаемый тип, сигнатура читается тяжело, а сообщение компилятора при ошибке — длинное и мало что объясняет.
Теперь опишем концепт
Integral, который явно говорит: тип должен быть целочисленным:template<typename T>
concept Integral = std::is_integral_v<T>;
Используем этот концепт в сигнатуре функции через
requires — ограничим шаблон только такими типами:template<Integral T>
T sum(T a, T b) {
return a + b;
}
И напишем простой
main, где одна строка корректна, а другая ломает ограничение концепта:int main() {
auto x = sum(1, 2); // ок, int — целочисленный тип
auto y = sum(1.0, 2.0); // ошибка: double не удовлетворяет Integral
return 0;
}Integral не выполняется для double», а сама сигнатура функции читается почти как обычный текст: шаблон sum работает только для целочисленных типов.Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍9🔥5
Как перестать писать first и second везде?
Классический код с
С C++17 можно «распаковать» пару в говорящие имена с помощью structured bindings:
🔥 То же самое работает для
📣 C++ Ready | #совет
Классический код с
std::pair и std::map обычно выглядит так:for (const auto& p : scores) {
std::cout << p.first << ": " << p.second << '\n';
}first/second ничего не говорят о смысле данных, а при чтении кода приходится всё время держать в голове, где имя, а где значение.С C++17 можно «распаковать» пару в говорящие имена с помощью structured bindings:
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << '\n';
}std::pair, std::tuple и многих стандартных типов: можно аккуратно разобрать результат функции на несколько переменных с понятными именами.Please open Telegram to view this post
VIEW IN TELEGRAM
👍25🔥8❤7👎1
Иногда функция ничего не находит, но вынуждена возвращать ноль или пустую строку; удобно дать этому состоянию отдельное, явно оформленное место.
В этом гайде:
• Как аккуратно выражать отсутствие результата в коде;
• Простой способ избежать магических значений ошибок;
• Научимся читать вызовы функций, не догадываясь по комментариям
Честно обозначенное «нет значения» делает интерфейсы проще, а ошибки поиска заметнее и предсказуемее.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14❤10🤝5👍4
Как перестать писать громоздкие циклы с индексами?
Классический вариант в C++ всё ещё часто выглядит так:
Здесь куча лишнего шума: тип индекса,
Легко ошибиться с границами (
С
Перебираем сразу элементы, а не индексы.
Тот же приём работает для
🔥 Используйте
📣 C++ Ready | #совет
Классический вариант в C++ всё ещё часто выглядит так:
for (std::size_t i = 0; i < names.size(); ++i) {
std::cout << names[i] << '\n';
}Здесь куча лишнего шума: тип индекса,
i < names.size(), доступ по names[i].Легко ошибиться с границами (
<= вместо <) или забыть обновить цикл, если контейнер поменялся.С
range-based for всё короче и безопаснее:for (const auto& name : names) {
std::cout << name << '\n';
}Перебираем сразу элементы, а не индексы.
Тот же приём работает для
std::vector, std::array, std::list, std::map и собственных контейнеров с begin()/end().range-based for по умолчанию, а циклы с индексом оставляйте только там, где действительно нужен номер элемента.Please open Telegram to view this post
VIEW IN TELEGRAM
🤝13❤7🔥5👍2
Разбираем небольшой планировщик дел, где каждая новая задача укладывается по важности так, что программа без сомнений знает, за что взяться первой.
В этом разборе:
• Посмотрим, как упорядочить дела так, чтобы важные всплывали первыми;
• Почувствуем, как код берёт на себя рутину выбора;
• Увидим, как из простых шагов собирается удобный маленький помощник.
После такого подхода список задач перестаёт пугать, а начать становится проще прямо сейчас для тебя.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16👍8❤5🤝4
Типобезопасные перечисления — enum class вместо старого enum!
Обычный
Сначала посмотрим на «старый»
Теперь перепишем на
🔥 Переход на
📣 C++ Ready | #практика
Обычный
enum неявно приводится к int и даёт сравнивать разные перечисления друг с другом — легко наделать странных багов.Сначала посмотрим на «старый»
enum и его проблемы:enum Color { Red, Green, Blue };
enum Status { Ok, Error };
int main() {
Color c = Red;
if (c == Ok) { } // компилируется, хотя Color и Status — разные enum'ы
}Теперь перепишем на
enum class, чтобы каждое перечисление стало отдельным типом с собственной областью имён:enum class Color { Red, Green, Blue };
enum class Status { Ok, Error };
int main() {
Color c = Color::Red;
Status s = Status::Ok;
// if (c == s) { } // ошибка компиляции: разные типы
}enum class не приводится к int по умолчанию, не пересекается по именам и сразу показывает, к какому именно набору значений относится переменная.enum class — простой шаг к более безопасному и читаемому коду с перечислениями.Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤9🔥4😁1
❤12👍6🔥5
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥24👍7❤5🤝1