WarptenLe 15/09/2018 à 00:43
Ca serait plus propre avec les concepts mais c'est TS C++20 donc je peux me gratter pour le moment
Le PLUS CHIANT c'est que les infos de genericite sont "perdues"... jmexplique,
template <typename T>
struct rebinder;
template <template <typename...> typename Base, typename... BaseArgs>
struct rebinder<Base<BaseArgs...>>
{
template <typename... Args>
using rebound = Base<Args...>;
};
(Exemple aligne en 2 secondes)
L'interet de ce truc c'est de pouvoir remapper des types a N parametres generiques en 1 ligne, p-ex
using pair_i2 = std::pair<int, int>;
using pair_f2 = typename rebinder<pair_i2>::template bound<float, float>; // Revient a std::pair<float, float>
L'exemple est evidemment completement con mais c'est la base pour les concepts.
Le seul probleme c'est que si maintenant j'ai une methode generique qui prend une pair<T1, T2>:
template <typename Pair>
void faire_choses_coquines_avec_paire(Paire const& paire) {
// Telephone rose du C++
}
Admettons que maintenant tu ne saches pas que c'est une paire, mais tu veux rebind vers pair<float, float> (Le compilo te casse la gueule si tu essaie de faire pair<float, float, float> etc)
template <typename Paire>
void faire_choses_pas_trop_jolies(Paire paire)
{
using paire_floats_t = typename rebinder<Paire>::template bound<float, float>;
paire_floats_t paire_floats_instance;
}
Ben ca compile pas. Pourquoi? Parce que le compilo voit qu'on veut un rebinder<T>, soit la classe de base, et va donc gentillement te dire que cette derniere n'a pas de membre nomme bound.
Du coup la solution? Ceci:
template <template <typename, typename> typename Paire, typename A, typename B>
void faire_choses_sales(Paire<A, B> const& paire)
{
using paire_floats = typename rebinder<Paire<A, B>>::template bound<float, float>;
paire_floats ffs;
}
Et la c'est tout bon, parce qu'on choisit la specialisation partielle qui expose bound.
Je pense pas que ce soit un bug pour autant, mais ca a du etre choisi comme ca pour pas rendre les compilo C++ encore plus usine-a-gaz-esque, et ca reste terriblement chiant. C'est pour ca que je me retrouve avec 4 specialisations partielles pour ma classe container_traits, parce que chaque container a un nombre differents de parametres generiques (pour le moment!), et que c'est pas evident d'aller chercher le N-ieme element d'un parameter pack (typename... Args), surtout quand N change d'un type a l'autre.
Par exemple la je suis coince car unordered_map expose un type dependant nomme value_type, qui vaut .... pair<const Clef, Valeur>. Mais a mes yeux, c'est un truc opaque, donc pour revenir a mon exemple, ben je peux pas reutiliser le type pair qu'un utilisateur foutrait dans sa librarie a facon STL, donc j'suis oblige d'imposer std::pair, et l'user se retrouve a jongler apres (operateurs de cast implicites / constructeurs par deplacement etc)
Bref c'est le bordel mais ca s'arrangera (faut esperer) avec les concepts, ou la on parlera plus telllement de templates mais plutot de features d'un type (constructible par copie, constructible par defaut, iterable dans un sens, dans les deux, etc)