Как пользоваться атрибутами из C++17
Вы читаете перевод статьи Using C++17 Attributes Today.
C++11 добавил в язык универсальные атрибуты, позволяющие сообщить компилятору дополнительную инфомацию о каком-либо символе или выражении в коде. В прошлом ведущие компиляторы предлагали для этого собственные механизмы, такие как __attribute__(deprecated)
в GNU GCC и LLVM/Clang и __declspec(deprecated)
в Visual C++. Универсальные атрибуты, они же ряды спецификации атрибутов, позволяют выражать то же самое независимым от компилятора способом, [[deprecated]]
.
Каждый атрибут попадает в стандарт отдельно. Атрибут
[[deprecated]]
(“устарело”) был стандартизирован в C++14, хотя в виде нестандартных расширений он был доступен в GCC, CLang и Visual C++ намного раньше. Если вы прячете атрибут за макросом, то для написания переносимого кода следует использовать простую форму атрибута, не принимающую аргументом строку с описанием причины, почему API устарел.
Новые атрибуты в C++17
В C++17 в стандарт попали три новых атрибута, которые позволяют управлять появлением различных предупреждений компилятора.
nodiscard
указывает, что возвращаемое функцией значение нельзя игнорировать и нужно сохранить в какую-либо переменнуюfallthrough
ставится перед case-веткой в switch и указывает, что в данном месте намеренно пропущенbreak
maybe_unused
заставляет компилятор погасить предупреждения о переменной, которая не используется в некоторых режимах компиляции (например, код возврата функции всего лишь проверяется вassert
)
К сожалению, стандарт C++17 на момент написания ещё только начали внедрять. Пройдут годы, прежде чем большинство программистов сможет свободно использовать атрибуты в production.
Обнаружение атрибутов
Одна из технических рекомендаций стандарта предлагает встроенный макрос для проверки доступности атрибута: __has_cpp_attribute
. Поддержка появилась в Clang с версии 3.6 и в GCC с версии 5. Если же макрос недоступен, можно добавить заглушку:
Применение атрибута nodiscard
В проектах с высокими требованиями к производительности могут практиковать отказ от выброса исключений (по крайней мере в некоторых компонентах). В таких случаях об ошибке выполнения операции сообщает код возврата, возвращённый из функции. Однако, очень легко забыть проверить этот код, и на помощь приходит nodiscard
.
Указать атрибут nodiscard
можно в объявлении функции:
Однако, если вы используете собственный класс для кодов ошибок, то вы можете указать атрибут только один раз в определении класса.
Это особенно полезно при включённой опции “warnings as errors” или при регулярной проверке отсутствия предупреждений на билдсервере. Вы ещё при комплиляции проверяете, что у вас имеется обработка ошибок.
Атрибут nodiscard был стандартизирован в C++17, но ещё до этого существовал под именем gnu::warn_unused_result
в компиляторах GCC с давних времён и Clang с версии 3.6. Используя механизм обнаружения атрибутов, вы можете объявить макрос, добавляющий атрибут nodiscard:
Применение атрибута fallthrough
Доставляет множество неудобств необходимость добавлять break
после каждого case
в конструкции switch
. Несмотря на то, что язык не поддерживает неявный break
, компилятор всё же может помочь. Атрибут fallthrough
был впервые введён в Clang под именем clang::fallthrough
в версии 3.6, а в C++17 он вошёл в более короткой форме. Мы также можем объявить макрос, добавляющий атрибут:
Чтобы воспользоваться преимуществами атрибута, в GCC и Clang следует включит предупреждение -Wimplicit-fallthrough
. После включения этой опции каждый case, не имеющий атрибута fallthrough, будет порождать предупреждение.
Заметим, что атрибут fallthrough должен быть внутри пустой инструкции, располагающейся непосредственно перед следующим case
Применение атрибута maybe_unused
Опытные программисты оставляют в коде инструкции assert
, чтобы обнаружить неожиданные входные данные в процессе разработки либо отладки. Иногда переменные создаются лишь для того, чтобы их использовали в assert
. В результате многие компиляторы выдают предупреждения о неиспользуемой переменной при сборке, например, в Release-конфигурации, где assert
обычно объявлен как пустой макрос.
Цель атрибута maybe_unused
— погасить предупреждения от отладочных переменных. Такое решение замещает старые, менее очевидные для новичков и более многословные решения.
Атрибуты из C++11 и C++14
В предыдущих версиях стандарта уже было внесено несколько атрибутов, но они не получили широкого распространения, видимо, из-за отсутствия поддержки в определённых компиляторах и малого числа атрибутов. Но теперь, с выходом C++17, эти атрибуты можно и нужно использовать чаще.
- атрибут
[[noreturn]]
применим только к объявлению функции и обозначает, что она не возвращает управление привычным путём; данным атрибутом помечен рад стандартных функций, включаяstd::abort
,std::exit
,std::quick_exit
,std::longjmp
,std::terminate
,std::rethrow_exception
. - атрибуты
[[deprecated]]
и[[deprecated("reason")]]
полезны для авторов библиотек: они порождают предупреждение об устаревшем API при попытке использования помеченных данным атрибутом функций, констант, переменных, классов, синонимов типов и перечислений.
Заключение
Поддержка атрибутов maybe_unused
, nodiscard
, fallthrough
из стандарта C++17 должна появиться в компиляторах уже в ближайшее время. Например, Clang 3.9 реализовал эти атрибуты и даже включает их при компиляции в режиме C++11 и выше. Если же вы не можете пользоваться новейшими компиляторами, то, по крайней мере, вы можете абстрагировать работу с атрибутами с помощью макросов.