if constexpr и универсальными лямбда-выражениями, чтобы код мог использовать тип, если он определен, при этом все еще принимаясь компилятором (и отбрасываясь) если тип не определен.
Однако в этом применении существует несколько проблем:
- Каждый раз нужно писать
struct. - Если тип не существует, то при присвоении ему имени этот тип вводится в текущее пространство имен, а не в пространство имен, в котором вы хотели, чтобы тип был.
- Нужно использовать
structс неквалифицированным именем. Нельзя использовать его для проверки типа, который вы не импортировали в текущее пространство имен.
Мы можем исправить все три проблемы с помощью одного решения: предварительно объявить тип в нужном пространстве имен.
// awesome.h namespace awesome { // может или может не содержать struct special { ... }; } // ваш код namespace awesome { // обеспечить объявление типов, которые мы детерминируем struct special; }
После того, как вы это сделали, вам не нужно писать struct, потому что структура была объявлена. Использование ее в качестве параметра типа шаблона в call_if_defined не приведет к созданию нового объявления, поскольку все уже было объявлено. И так как она была объявлена, вы можете получить к ней доступ через ее неквалифицированное имя, ее полное имя пространства имен или через что-либо между ними. Также через псевдоним типа или зависимый тип (к сожалению, они не между).
namespace app { void foo() { call_if_defined<awesome::special>([&](auto* p) { // Этот код компилируется только если "awesome::special" // определен. Создайте локальное имя для "special" // выведя его из фиктивного параметра. using special = std::decay_t<decltype(*p)>; // Теперь можно использовать локальное имя "special" для доступа // к параметрам "awesome::special". special::do_something(); }); } }
Те, кто следил за данной серией статей с самого начала, могли заметить, что метод call_if_defined не совсем совпадает с версией, которую мы написали ранее. Новая версия поддерживает несколько параметров типа и вызывает лямбду, только если определены все типы.
Посмотрим ближе:
template<typename... T, typename TLambda> void call_if_defined(TLambda&& lambda) { if constexpr ((... && is_complete_type_v<T>)) { lambda(static_cast<T*>(nullptr)...); } }
Двойные скобки в if constexpr ((…)) выглядят странно, но они обязательны. Внешние скобки требуются оператором if constexpr, а внутренние скобки требуются выражением свертки. Выражение свертки расширяется до
if constexpr ( (is_complete_type_v<T1> && is_complete_type_v<T2> && ... is_complete_type_v<Tn>))
Вызов лямбды использует расширение пакета параметров:
lambda(static_cast<T*>(nullptr)...);
Это расширяется до
lambda(static_cast<T1*>(nullptr), static_cast<T2*>(nullptr), ..., static_cast<Tn*>(nullptr));
где static_cast<T*>(nullptr) повторяется один раз для каждого типа.
Как я уже отмечал ранее, мы можем использовать эту функцию для вызова лямбды, если определены все типы:
void foo(Source const& source) { call_if_defined<special, magic>( [&](auto* p1, auto* p2) { using special = std::decay_t<decltype(*p1)>; using magic = std::decay_t<decltype(*p2)>; auto s = source.try_get<special>(); if (s) magic::add_magic(s); }); }
C++20 позволяет записать это так:
void foo(Source const& source) { call_if_defined<special, magic>( [&]<typename special, typename magic> (special*, magic*) { auto s = source.try_get<special>(); if (s) magic::add_magic(s); }); }
что позволяет вам называть тип шаблона, тем самым избавляя вас от необходимости заново извлекать его, играя с std::decay_t.
В следующей статье мы будем использовать эту как трамплин и расширять схему.
Примечание: это четвертая часть основной серии статей, но еще есть и другие части (1,2,3,5). Для нетерпеливых: вот, что нужно скопировать и вставить:
template<typename, typename = void> constexpr bool is_type_complete_v = false; template<typename T> constexpr bool is_type_complete_v <T, std::void_t<decltype(sizeof(T))>> = true; template<typename... T, typename TLambda> void call_if_defined(TLambda&& lambda) { if constexpr ((... && is_complete_type_v<T>)) { lambda(static_cast<T*>(nullptr)...); } }
Кстати, у нас есть классная вакансия
Более десяти лет Havok находится на острие инноваций в разработке игр и интерактивного 3D. Как часть Cognition, команды, отвечающей за HoloLens, мы теперь комбинируем эту экспертизу и силу облака Azure для разработки многих новых захватывающих сервисов, поддерживающих смешанную реальность. Среди них недавно анонсированная служба удаленного рендеринга Azure (Azure Remote Rendering). Мы увлечены совмещением AR, VR и облачных технологий, которые вместе позволяют создавать инновационные практики использования смешанной реальности.
Работа в Havok:
- Вы будете работать в небольших командах с талантливыми разработчиками
- У вас будет возможность работать с новыми технологиями на самых разных аппаратных платформах и устройствах
- Вы будете работать над решением сложных технических задач, имеющих огромные перспективы
- Вы будете сотрудничать с крутыми командами по всему миру
Обязанности
- Проектировать, разрабатывать и тестировать высококачественный, эффективный и чистый мультиплатформенный С++ код
- Разрабатывать хорошо-масштабируемые сервисы Azure
- Работать напрямую с внутренними и внешними заказчиками, чтобы стимулировать разработку продукта
Квалификации
- Навыки программирования и отладки на C++
- Умение работать в команде с общим кодом
- Опыт работы с облачными и распределенными сервисными технологиями (например, Azure Batch, Azure Blob Storage, Docker, Telemetry)
Будет плюсом
- C#, ASP.Net, JavaScript, TypeScript, React
- Unity, Unreal или связанные игровые движки
- Опыт в интерактивном 3D, AR или VR
- Сетевые и серверные сервисы
- Оптимизация производительности
Вы можете узнать больше и подать свою заявку здесь.
ссылка на оригинал статьи https://habr.com/ru/company/microsoft/blog/459787/

Добавить комментарий