J'ai un problème avec la spécialisation partielle impliquant des paramètres de modèle variadique. La spécialisation avec un préfixe
#include <iostream> template<char ... C> struct Str { static constexpr char Value[] = { C..., '\0' }; }; template<char ... C> constexpr char Str<C...>::Value[]; template<typename> struct TrimFront; template<char A, char ... C> struct TrimFront<Str<A, C...>> { typedef Str<C...> Type; }; template<typename> struct TrimBack; template<char A, char ... C> struct TrimBack<Str<C..., A>> { typedef Str<C...> Type; }; int main(int, char **) { typedef Str<'a', 'b', 'c', 'd', 'e', 'f'> str_t; std::cout << str_t::Value << std::endl; // prints "abcdef" std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef" std::cout << TrimBack<str_t>::Type::Value << std::endl; // ERROR (incomplete type) return 0; }
fonctionne comme prévu, mais quand j'essaie de faire correspondre avec le suffixe
template<typename A, typename ... B> struct Foo<B..., A> { };
cela ne fonctionne pas. Y a-t-il une règle que je ne connais pas ou est-ce un problème de compilation? (J'utilise G ++ 7.4, spécifiquement x86_64-w64-mingw32-g ++ de cygwin)
Exemple autonome pour démontrer mon problème:
template<typename A, typename ... B> struct Foo<A, B...> { };
3 Réponses :
Voici une solution utilisant boost :: mp11
:
Commentaires en ligne:
#include <iostream> #include <boost/mp11.hpp> using namespace boost::mp11; template<char c> struct c_char { static constexpr char value() { return c; } }; template<typename...> struct Str; template<char... C> struct Str<c_char<C>...> { static constexpr auto size() -> std::size_t { return sizeof...(C) + 1; } static constexpr char Value [size()] = { C..., '\0' }; }; template<char...C> using make_Str = Str<c_char<C>...>; template<typename List> struct TrimFront { using Type = mp_pop_front<List>; }; template<typename List> struct TrimBack { using Type = mp_reverse<mp_pop_front<mp_reverse<List>>>; }; int main(int, char **) { using str_t = make_Str<'a', 'b', 'c', 'd', 'e', 'f'>; std::cout << str_t::Value << std::endl; // prints "abcdef" std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef" std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde" return 0; }
Résultat attendu:
abcdef bcdef abcde
http://coliru.stacked-crooked.com/a/ 387e5dc7ef262f1f
Grâce à nos nouvelles connaissances, nous pouvons simplifier:
#include <iostream> #include <boost/mp11.hpp> template<char ... C> struct Str { static constexpr char Value[] = { C..., '\0' }; }; template<char ... C> constexpr char Str<C...>::Value[]; template<typename> struct TrimFront; template<char A, char ... C> struct TrimFront<Str<A, C...>> { typedef Str<C...> Type; }; template<typename> struct TrimBack; using namespace boost::mp11; // a means of turning chars into types template<char c> struct c_char { constexpr char value() { return c; } }; // a means of turning an mp_list of c_char<char>... back into a Str<char...> template<typename> struct back_to_Str; template<char...cs> struct back_to_Str<mp_list<c_char<cs>...>> { using result = Str<cs...>; }; // TrimBack using types as computation steps: template<char... C> struct TrimBack<Str<C...>> { // turn the input chars into an mp_list of c_char // always use types, they're much easier than values when metaprogramming using input = mp_list<c_char<C>...>; // reverse the list using reversed = mp_reverse<input>; // pop the front c_char<> using popped = mp_pop_front<reversed>; // reverse again using re_reversed = mp_reverse<popped>; // turn back into a Str<char...> using Type = typename back_to_Str<re_reversed>::result; }; int main(int, char **) { typedef Str<'a', 'b', 'c', 'd', 'e', 'f'> str_t; std::cout << str_t::Value << std::endl; // prints "abcdef" std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef" std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde" return 0; }
merci pour votre réponse détaillée, mais j'ai bien peur que cela ne réponde pas à ma question. je sais comment résoudre le problème que j'ai donné (modèle variadic -> type list -> opération de manipulation de liste arbitraire -> modèle variadic), mais ce n'était qu'un exemple pour clarifier ma question: puis-je me spécialiser partiellement avec un pack d'arguments variadiques ce n'est pas à la fin: my_struct
? j'essaye d'éviter la solution de type-list parce que dans le cas d'utilisation réel, le modèle variadic contient plusieurs centaines de types; reverse -> pop -> reverse aurait tout à fait la pénalité de performance
@DaveDanielKessener Je vois. Je me demande s'il y a quelque chose qui pourrait être fait avec index_sequence et tuples.
Je suppose que
template<char A, char ... C> struct TrimBack<Str<C..., A>> { typedef Str<C...> Type; };
ne peut pas fonctionner (" A
" et " C ...
") ne peut pas être déduit car le pack variadique ( C ...
) n'est pas en dernière position.
L'OP, raisonnablement, demande une référence
vraiment? très malheureux. pouvez-vous par hasard indiquer où cela est dit dans la norme? je n'arrive pas à trouver la partie pertinente
Je ne suis pas une couche de langage mais il me semble que la partie pertinente (standard C ++ 11) est 14.8.2.5 ("Déduire l'argument modèle d'un type", "[temp.deduct.type] "), point 9 (c'est moi qui souligne)
Si
P
a une forme qui contientou
, alors chaque argument
P_i
de la liste d'arguments de modèle respectiveP
est comparée à l'argument correspondantA_i
de la liste d'arguments de modèle correspondante deA
. Si la liste d'arguments de modèle deP
contient une extension de pack qui n'est pas le dernier argument de modèle, toute la liste d'arguments de modèle est un contexte non déduit . SiP_i
est une extension de pack, alors le modèle deP_i
est comparé à chaque argument restant dans la liste d'arguments de modèle deA
. Chaque comparaison déduit des arguments de modèle pour les positions suivantes dans les packs de paramètres de modèle développés parP_i
.
Donc, si je ne me trompe pas, TrimBack
(aka TrimBack
) donne une erreur car
1) dans une première phase, Str
correspond Str
2) mais dans une deuxième phase, en essayant de déduire les types C ...
et A
, P
(c'est-à-dire Str
, dans cette phase) "contient une extension de pack qui n'est pas le dernier argument du modèle", donc "la liste complète des arguments du modèle est un contexte non déduit".
Une spécialisation partielle de modèle de classe comme celle-ci
std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde"
n'est pas autorisée, car pour déduire C ...
et A
, d'un type est effectuée, et un argument pack qui n'est pas le dernier en fait un contexte non déduit .
Ce que vous pouvez faire à la place, c'est utiliser un type d'aide pour "déballer" le pack, puis le "reconditionner", moins le dernier élément .
template <char ...P> struct dummy {}; template <class T, char ...P> struct internal; template <char ...P1, char T, char ...P2> struct internal<dummy<P1...>, T, P2...> { using type = typename internal<dummy<P1..., T>, P2...>::type; // unwrap one recursively }; template <char ...P1, char T> struct internal<dummy<P1...>, T> { using type = Str<P1...>; // re-wrap all but the last one }; template <typename> struct TrimBack; template <char ...C> struct TrimBack<Str<C...>> { using Type = typename internal<dummy<>, C...>::type; };
Cela devrait maintenant fonctionner:
template<typename> struct TrimBack; template<char ...C, char A> struct TrimBack<Str<C..., A>> {}
J'avoue que je suis confus par la norme mais ... êtes-vous sûr que (8.5) de 14.5.5 s'applique dans ce cas? Je veux dire ... dans ce cas, "argument" est " Str
" ou " C ...
"?
J'avais peur qu'il ne soit pas légal de faire correspondre un pack de paramètres de modèle variadique qui n'est pas à la fin. merci pour la solution proposée, mais comme la réponse de Richard Hodges, une approche avec une complexité O (N) n'est pas idéale.
Je crains qu'il n'y ait malheureusement pas de solution O (1), au mieux vous pouvez le rendre O (logN) avec un TMP intelligent, mais ça va être beaucoup moins lisible. N'oubliez pas que nous ne parlons que du travail de compilation. @ max66 Je pense que vous avez raison, j'ai mis à jour ma réponse.
Je suis d'accord avec la première référence (14.8.2.5 point 1, "déduction d'un type") mais la seconde (point 5.6, "contect non déduit") concerne les fonctions
le problème est qu'une liste variadique déduite doit être en dernière position; donc
A, B ...
est ok,A ..., B
ne fonctionne pas.@ max66 vraiment? très malheureux. pouvez-vous par hasard indiquer où cela est dit dans la norme? je n'arrive pas à trouver la partie pertinente.
J'avoue que je ne suis pas juriste en langues; mais j'ai ajouté une réponse en essayant de répondre à votre question. J'espère que cela aide.