if constexpr
and universal lambda expressions so that the code can use the type if it is defined while still being adopted by the compiler (and discarded) if the type is not defined.struct
.struct
with an unqualified name. You cannot use it to check for a type that you did not import into the current namespace. // awesome.h namespace awesome { // struct special { ... }; } // namespace awesome { // , struct special; }
struct
because the structure has been declared. Using it as a template type parameter in call_if_defined
will not create a new ad, since everything has already been announced. And since she was declared, you can access her through her unqualified name, her full namespace name, or something in between. Also through the type alias or dependent type (unfortunately, they are not between). 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
method call_if_defined
not quite coincide with the version we wrote earlier. The new version supports several type parameters and causes lambda only if all types are 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
operator, and the inner brackets are required by the convolution expression . The convolution expression expands to 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)
repeated once for each type. 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); }); }
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
. 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)...); } }
Source: https://habr.com/ru/post/459787/
All Articles