We present programming techniques to illustrate the facilities and principles of C++ generic programming using concepts. Concepts are C++'s way to express constraints on generic code. As an initial example, we provide a simple type system that eliminates narrowing conversions and provides range checking without unnecessary notational or run-time overheads. Concepts are used throughout to provide user-defined extensions to the type system. The aim is to show their utility and the fundamental ideas behind them, rather than to provide a detailed or complete explanation of C++'s language support for generic programming or the extensive support provided by the standard library. Generic programming is an integral part of C++, rather than an isolated sub-language. In particular, key facilities support general programming as well as generic programming (e.g., uniform notation for types, lambdas, variadic templates, and C++26 static reflection). Finally, we give design rationales and origins for key parts of the concept design, including use patterns, the relationship to Object-Oriented Programming, value arguments, notation, concept type-matching, and definition checking.
В данной статье демонстрируются методы программирования, использующие концепции (concepts) для иллюстрации средств и принципов обобщённого программирования на C++. Концепции представляют собой способ выражения ограничений обобщённого кода в C++. В качестве начального примера статья предоставляет простую систему типов, которая исключает сужающие преобразования и обеспечивает проверку диапазонов без ненужных накладных расходов на символы или время выполнения. Концепции широко используются для предоставления расширений пользовательских систем типов. Статья направлена на демонстрацию практичности концепций и лежащих в их основе фундаментальных идей, а не на предоставление подробного или полного объяснения языковой поддержки обобщённого программирования в C++ или широкой поддержки стандартной библиотеки. Обобщённое программирование является составной частью C++, а не изолированным подъязыком. Наконец, статья представляет философию проектирования и происхождение ключевых аспектов проектирования концепций, включая паттерны использования, отношение к объектно-ориентированному программированию, параметры значений, символическое представление, сопоставление типов концепций и проверку определений.
Вызовы обобщённого программирования: Традиционное обобщённое программирование на C++ страдает от отсутствия явных спецификаций интерфейсов, что приводит к неясным сообщениям об ошибках компиляции, которые сложны для понимания как программистами, так и компилятором.
Проблемы безопасности типов: C++ унаследовал правила неявного преобразования типов из языка C, особенно сужающие преобразования между арифметическими типами (например, преобразование из большого целого типа в меньший может привести к потере информации), что является важным источником ошибок и проблем безопасности.
Отсутствие проверки диапазонов: Традиционное использование указателей и массивов легко приводит к проблемам безопасности, таким как переполнение буфера, и отсутствуют эффективные механизмы проверки диапазонов.
Статья следует целям обобщённого программирования, предложенным Алексом Степановым: "наиболее универсальное, наиболее эффективное, наиболее гибкое представление концепций" и удовлетворяет следующим требованиям проектирования:
Универсальность: Должна быть возможность выражения гораздо большего, чем можно представить
Непоколебимая эффективность: Обобщённый код не должен иметь накладных расходов на время выполнения по сравнению с эквивалентным низкоуровневым кодом
Интерфейс со статической типобезопасностью: Система типов должна быть достаточно гибкой, чтобы позволить проверку на этапе компиляции большинства аспектов интерфейсов, которые не зависят от значений времени выполнения
Предложены методы программирования на основе концепций для типобезопасности: Демонстрируется, как использовать концепции для исключения сужающих преобразований и обеспечения проверки диапазонов при сохранении нулевых накладных расходов на время выполнения.
Построены практические расширения систем типов:
Реализован тип Number<T>, исключающий опасные сужающие преобразования
Разработан безопасный тип Span, обеспечивающий проверку диапазонов при доступе к массивам
Продемонстрировано применение концепций в проектировании алгоритмов
Предоставлена глубокая философия проектирования концепций: Подробно описаны решения проектирования концепций как функций времени компиляции, отношение к объектно-ориентированному программированию, выбор символического представления и другие ключевые соображения проектирования.
Продемонстрирована единство обобщённого программирования: Доказано, что обобщённое программирование является составной частью C++, а не изолированным подъязыком, и беспрепятственно интегрируется с другими языковыми особенностями (такими как лямбда-функции, вариадические шаблоны, статическая рефлексия).
template<Num U, Num T>
constexpr bool will_narrow(U u) {
if constexpr (!Can_narrow_to<T, U>)
return false;
// проверка во время выполнения только при возможности сужения
T t = u;
return (t != u);
}
Через проверку на этапе компиляции и условную компиляцию достигается нулевые накладные расходы на время выполнения:
template<Num U, Num T>
constexpr bool will_narrow(U u) {
if constexpr (!Can_narrow_to<T, U>)
return false; // определено на этапе компиляции, без затрат на время выполнения
// проверка во время выполнения только при необходимости
}
Статья указывает, что многие системы ограничений полагаются на наборы функций (подобные определениям классов), тогда как концепции C++ используют функциональный подход, обеспечивающий большую гибкость.
Высокая практичность: Предоставляет методы программирования, которые можно непосредственно применять для решения реальных проблем типобезопасности.
Сочетание теории и практики: Имеет глубокую теоретическую основу (математические концепции) и конкретные примеры реализации.
Проектирование с нулевыми накладными расходами: Благодаря тщательному проектированию проверок на этапе компиляции достигается типобезопасность без потери производительности.
Всестороннее рассмотрение проектирования: Охватывает полное пространство проектирования от базового синтаксиса до продвинутых функций (таких как статическая рефлексия).
Авторитетность: Автор Bjarne Stroustrup является создателем языка C++ и обладает неоспоримым авторитетом.
Крутая кривая обучения: Синтаксис концепций и паттерны использования могут быть сложными для начинающих.
Влияние на время компиляции: Большое количество проверок на этапе компиляции может увеличить время компиляции, что недостаточно обсуждается в статье.
Вызовы обратной совместимости: Хотя совместимость сохранена, смешанное использование нового и старого кода может вызвать путаницу.
Качество сообщений об ошибках: Хотя концепции улучшают сообщения об ошибках, сообщения об ошибках для сложных концепций всё ещё могут быть трудны для понимания.
Статья содержит богатую библиографию, охватывающую различные аспекты от проектирования языка C++ до теории обобщённого программирования, включая в основном:
Пионерские работы Алекса Степанова по STL и обобщённому программированию
Технические отчёты и предложения комитета по стандартизации C++
Исторические документы автора о проектировании и эволюции C++
Соответствующие исследования в области теории языков программирования
Резюме: Это статья с важной теоретической и практической ценностью, написанная самим создателем языка C++, всесторонне демонстрирующая применение концепций в обобщённом программировании на C++. Статья не только предоставляет практические методы программирования, но и глубоко раскрывает философию проектирования, имея важное справочное значение как для программистов на C++, так и для исследователей в области языков программирования.