Reliure structurée largeur

Est-il possible de déterminer combien de noms de variables dois-je spécifier entre crochets à l’aide de la syntaxe des liaisons structurées afin de faire correspondre le nombre de données membres d’une struct droite?

Je veux faire une partie de la bibliothèque générique, qui utilise des liaisons structurées pour décomposer des classes arbitraires en ses constituants. Pour le moment, il n’existe pas de version variadique des liaisons structurées (et, je pense, cela ne peut pas être proposé pour la syntaxe actuelle), mais ma première idée est de créer un ensemble de surcharges de certaines fonctions decompose() , qui effectue la décomposition du paramètre struct en un ensemble de ses constituants. decompose() devrait être surchargé par le nombre de données membres du paramètre (qui est struct ). Actuellement, constexpr if syntaxe peut également être utilisé pour le dissortingbuer. Mais comment puis-je imiter quelque chose de similaire à sizeof... operator aux fins susmentionnées? Je ne peux pas utiliser la syntaxe auto [a, b, c] quelque part dans les constructions SFINAE, car il s’agit d’ une déclaration de décomposition et AFAIK, aucune déclaration ne peut être utilisée dans decltype , je ne peux pas l’utiliser pour mes besoins dans le corps des fonctions lambda car Les fonctions lambda ne peuvent pas non plus être utilisées dans les arguments de modèles.

Bien sûr, je veux avoir un opérateur intégré (avec une syntaxe telle que sizeof[] S / sizeof[](S) pour la class S ), mais une chose comme ce qui suit est également acceptable:

 template struct sizeof_struct { }; template struct sizeof_struct< type, std::void_t< decltype([] { auto && [p1] = std::declval(); void(p1); }) > > : std::integral_constant { }; template struct sizeof_struct< type, std::void_t< decltype([] { auto && [p1, p2] = std::declval(); void(p1); void(p2); }) > > : std::integral_constant { }; ... etc up to some reasonable arity 

Peut-être que constexpr lambda nous permettra de les utiliser dans les arguments de template. Qu’est-ce que tu penses?

Sera-ce possible avec les prochains concepts ?

 struct two_elements { int x; double y; }; struct five_elements { std::ssortingng one; std::unique_ptr two; int * three; char four; std::array five; }; struct anything { template operator T()const; }; namespace details { template struct can_construct_with_N:std::false_type {}; template struct can_construct_with_N, std::void_t< decltype(T{(void(Is),anything{})...}) >>: std::true_type {}; } template using can_construct_with_N=details::can_construct_with_N>; namespace details { templateclass target> struct maximize: std::conditional_t< maximize{} == (Min+Range/2)-1, maximize, maximize > {}; templateclass target> struct maximize: std::conditional_t< target{}, std::integral_constant, std::integral_constant > {}; templateclass target> struct maximize: std::integral_constant {}; template struct construct_searcher { template using result = ::can_construct_with_N; }; } template using construct_airity = details::maximize< 0, Cap, details::construct_searcher::template result >; 

Ceci fait une recherche binary pour la plus longue construction de T de 0 à 20. 20 est une constante, vous pouvez l’augmenter comme vous voulez, à la compilation et à un coût en mémoire.

Exemple en direct .

Si les données de votre structure ne peuvent pas être construites à partir d’une rvalue de son propre type, cela ne fonctionnera pas en C ++ 14, mais je crois que l’élision est garantie en C ++ 17 ici (!)

Transformer cela en liaisons structurées nécessite plus qu’une stack de code manuel. Mais une fois que vous en avez, vous devriez pouvoir poser des questions du type “quel est le 3ème type de cette struct “, etc.

Si une struct peut être décomposée en liaisons structurées sans l’ tuple_size la commande tuple_size , sa précision détermine le nombre de variables dont elle a besoin.

Malheureusement, std::tuple_size n’est pas compatible avec SFINAE, même en C ++ 17. Cependant, les types qui utilisent la partie tuple_size doivent également activer ADL-enable std::get .

Créez un espace de noms avec un failure_tag get(Ts const&...) qui using std::get . Utilisez cela pour détecter s’ils ont été surchargés et get<0> sur le type ( !std::is_same< get_type, failure_tag >{} ) et, le cas échéant, descendez le chemin du tuple_element pour déterminer l’air. Farcissez les éléments résultants dans un std::tuple de decltype(get(x)) et renvoyez-le.

Si cela échoue, utilisez la propriété construct_airity ci-dessus et utilisez-la pour comprendre comment utiliser des liaisons structurées sur le type. J’enverrais probablement cela dans un std::tie , par souci d’uniformité.

Nous avons maintenant tuple_it qui prend tout ce qui est structuré-contraignant et le convertit en un tuple de références ou de valeurs. Les deux chemins ont maintenant convergé et votre code générique est plus simple!

Il existe également une approche linéaire pour trouver “l’arité globale” (même si, dans les mêmes limites assez ssortingctes, comme dans la réponse acceptée):

 #include  #include  #include  struct filler { template< typename type > operator type && (); }; template< typename aggregate, typename index_sequence = std::index_sequence<>, typename = void > struct aggregate_arity : index_sequence { }; template< typename aggregate, std::size_t ...indices > struct aggregate_arity< aggregate, std::index_sequence< indices... >, std::void_t< decltype(aggregate{(indices, std::declval< filler >())..., std::declval< filler >()}) > > : aggregate_arity< aggregate, std::index_sequence< indices..., sizeof...(indices) > > { }; template< std::size_t index, typename type > constexpr decltype(auto) get(type & value) noexcept { constexpr std::size_t arity = aggregate_arity< std::remove_cv_t< type > >::size(); if constexpr (arity == 1) { auto & [p1] = value; if constexpr (index == 0) { return (p1); } else { return; } } else if constexpr (arity == 2) { auto & [p1, p2] = value; if constexpr (index == 0) { return (p1); } else if constexpr (index == 1) { return (p2); } else { return; } } else if constexpr (arity == 3) { auto & [p1, p2, p3] = value; if constexpr (index == 0) { return (p1); } else if constexpr (index == 1) { return (p2); } else if constexpr (index == 2) { return (p3); } else { return; } } else /* extend it by yourself for higher arities */ { return; } } // main.cpp #include  #include  namespace { using S = struct { int i; char c; bool b; }; S s{1, '2', true}; decltype(auto) i = get< 0 >(s); decltype(auto) c = get< 1 >(s); decltype(auto) b = get< 2 >(s); static_assert(std::is_same< decltype(i), int & >{}); static_assert(std::is_same< decltype(c), char & >{}); static_assert(std::is_same< decltype(b), bool & >{}); static_assert(&i == &s.i); static_assert(&c == &s.c); static_assert(&b == &s.b); } int main() { assert(i == 1); assert(c == '2'); assert(b == true); return EXIT_SUCCESS; } 

Actuellement, l’argument de get() ne peut pas avoir le qualificateur de type de niveau supérieur const (le type peut être && et & , mais pas const & et const && ), à cause du bogue .

Exemple en direct .