C ++, se o tipo está definido: declaração preliminar dos objetos necessários

Na última vez , usamos o SFINAE para descobrir se um tipo tem uma definição e o usamos em combinação com if constexpr e expressões lambda genéricas, para que o código possa usar o tipo se for definido, enquanto ainda estiver sendo aceito pelo compilador (e descartado) se o tipo não estiver definido.

No entanto, existem vários problemas com este aplicativo:

  • Toda vez que você precisa escrever uma struct .
  • Se o tipo não existir, quando você o nomear, esse tipo será inserido no espaço para nome atual e não no espaço para nome em que você deseja que o tipo seja.
  • Você precisa usar uma struct com um nome não qualificado. Você não pode usá-lo para verificar um tipo que não importou para o espaço para nome atual.

Podemos resolver todos os três problemas com uma solução: primeiro declare o tipo no espaço para nome desejado.



 // awesome.h namespace awesome { //      struct special { ... }; } //   namespace awesome { //   ,    struct special; } 

Depois de fazer isso, você não precisa escrever uma struct porque a estrutura foi declarada. Usá-lo como um parâmetro do tipo de modelo em call_if_defined não levará à criação de uma nova declaração, pois tudo já foi declarado. E desde que ela foi declarada, você pode acessá-la através do nome não qualificado, do nome completo do espaço para nome ou de qualquer outro meio. Também através de um alias de tipo ou de um tipo dependente (infelizmente, eles não estão entre).

 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(); }); } } 

Quem acompanha essa série de artigos desde o início pode ter notado que o método call_if_defined não corresponde exatamente à versão que escrevemos anteriormente. A nova versão suporta vários parâmetros de tipo e chama um lambda apenas se todos os tipos estiverem definidos.

Vamos dar uma olhada:

 template<typename... T, typename TLambda> void call_if_defined(TLambda&& lambda) { if constexpr ((... && is_complete_type_v<T>)) { lambda(static_cast<T*>(nullptr)...); } } 

Os colchetes duplos em se constexpr ((...)) parecem estranhos, mas são necessários. Parênteses externos são requeridos pela if constexpr e parênteses internos são requeridos por uma expressão de convolução . A expressão de convolução se expande para

  if constexpr ( (is_complete_type_v<T1> && is_complete_type_v<T2> && ... is_complete_type_v<Tn>)) 

A chamada lambda usa a extensão do pacote de parâmetros :

  lambda(static_cast<T*>(nullptr)...); 

Expande para

  lambda(static_cast<T1*>(nullptr), static_cast<T2*>(nullptr), ..., static_cast<Tn*>(nullptr)); 

onde static_cast<T*>(nullptr) repetido uma vez para cada tipo.

Como observei anteriormente, podemos usar esta função para chamar o lambda se todos os tipos estiverem definidos:

 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); }); } 

O C ++ 20 permite que você escreva assim:

 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); }); } 

que permite nomear o tipo do modelo, eliminando assim a necessidade de extraí-lo novamente ao jogar com std::decay_t .

No próximo artigo, usaremos isso como um trampolim e estenderemos o circuito.



Nota : esta é a quarta parte da série principal de artigos, mas ainda existem outras partes ( 1 , 2 , 3 , 5 ). Para os impacientes: eis o que você precisa para copiar e colar:

 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)...); } } 

By the way, temos um trabalho legal


Por mais de uma década, a Havok está na vanguarda da inovação no desenvolvimento de jogos e em 3D interativo. Como parte da Cognition, a equipe HoloLens, agora estamos combinando esse conhecimento e o poder da nuvem do Azure para desenvolver muitos novos serviços emocionantes de realidade mista. Entre eles está o serviço de renderização remota do Azure anunciado recentemente. Somos apaixonados pela combinação de tecnologias de RA, VR e nuvem, que juntas nos permitem criar práticas inovadoras usando realidade mista.

Trabalhos na Havok:

  • Você trabalhará em pequenas equipes com desenvolvedores talentosos
  • Você terá a oportunidade de trabalhar com novas tecnologias em uma variedade de plataformas e dispositivos de hardware.
  • Você trabalhará na solução de problemas técnicos complexos com grandes perspectivas
  • Você colaborará com equipes legais em todo o mundo.

Responsabilidades


  • Projete, desenvolva e teste código C ++ multiplataforma eficiente, limpo e de alta qualidade
  • Desenvolver serviços do Azure bem escalonáveis
  • Trabalhe diretamente com clientes internos e externos para estimular o desenvolvimento de produtos

Qualificações


  • Habilidades de programação e depuração em C ++
  • Capacidade de trabalhar em equipe com um código comum
  • Experiência com nuvem e tecnologias de serviço distribuído (por exemplo, Lote do Azure, Armazenamento de Blob do Azure, Docker, Telemetria)

Será um plus


  • C #, ASP.Net, JavaScript, TypeScript, React
  • Unity, motores de jogo irreais ou relacionados
  • Experimente 3D interativo, AR ou VR
  • Serviços de rede e servidor
  • Otimização de desempenho

Você pode saber mais e enviar sua inscrição aqui ou através do LinkedIn .

Source: https://habr.com/ru/post/pt459787/


All Articles