Je lisais les réponses à "Impression de 1 à 1000 sans boucle ou des conditionnels " et je me demande pourquoi il est nécessaire d'avoir le cas spécial pour le numéro de numéro de numéro dans la réponse supérieure.
Si je supprimais cela et ajoutez une vérification pour n == 1 dans le modèle ( Code ci-dessous), le code échoue la compilation avec "La profondeur de l'instanciation de modèle dépasse le maximum" mais je ne sais pas pourquoi. Les conditionnels sont-ils traités différemment dans la compilation? P>
#include <iostream> template<int N> struct NumberGeneration { static void out(std::ostream& os) { if (N == 1) { os << 1 << std::endl; } else { NumberGeneration<N-1>::out(os); os << N << std::endl; } } }; int main() { NumberGeneration<1000>::out(std::cout); }
8 Réponses :
C'est parce que les entiers peuvent être un code négatif et d'exécution (le En bref, tout comme tout em> la récursion, vous devez fournir votre propre cas de base. P> si code> vérifier) ne pas arrêter le compilateur instanciant du modèle avec 0, -1, -2, etc. Un compilateur pourrait EM> Être capable de sortir avec ce que vous proposez, mais si vous insultiez les autres modèles (0, -1, ...) a des effets secondaires que vous dépendez? Le compilateur ne peut pas manquer de les instantiez pour vous dans ce cas. P>
Je me demande pourquoi il est nécessaire d'avoir le cas particulier pour Numéro génération <1> dans la réponse supérieure. P> blockQuote>
Parce que c'est la condition finale de récursif! Sans que cela puisse la fin récursive? P>
Qu'en est-il du si n == 1 conditionnel j'ai ajouté au modèle? Dans ce cas, nous n'instenus pas un nouveau avec N-1, alors c'est comme ça que je m'attends à ce que la récursive s'arrête.
@ baris.m: Vous ne devez pas supprimer le modèle de spécialisation de la condition 1, car c'est la condition de fin de la compilation pour mettre fin à la calcation / compilation.
@ baris.m: la condition que vous ajoutez n'a pas d'aide pour compiler cela ne se souciera pas de cela et de mettre fin à la compilation pendant la compilation. Le chèque que vous avez ajouté n'est utile que pendant l'heure d'exécution, pas de compilation.
@ baris.m: Veuillez lire ceci si vous avez toujours un doute: en.wikipedia.org/wiki/template_metaProgramming
La génération de code et la compilation ne suprent pas en fonction des conditionnels! Considérons ceci: si vous ne déclarez jamais En effet, la statique L'analogue des conditionnels est précisément spécialisation de modèles: p> bar () code>, il s'agit d'une erreur
NumberGeneration
Merci, cela a du sens, je ne pensais pas vraiment à l'instanciation du temps compilé et m'attendait à ce que cela ne soit fait que lorsque le code est atteint.
@ baris.m: Mais il y a une subtilité cruciale de ce que vous entendez par "Le code est atteint": il est atteint une fois lors de la compilation par le compilateur, toujours, puis sur conditionnellement i> au moment de l'exécution du programme.
Dans mon commentaire, je parlais d'être atteint au moment de l'exécution. Votre réponse est parfaitement logique.
Le conditionnel Ainsi, même pour N = 1, le compilateur générera NumberGenerator <0>, puis NumberGenerator <-1> ... sans fin, jusqu'à atteindre une profondeur d'instanciation de modèle. P> si code> ne sera pas traité au moment de la compilation. Il sera traité à l'exécution. P>
En général, la condition n == 1 code> dans votre code est évaluée au moment de l'exécution (bien que le compilateur puisse l'optimiser à l'extérieur), pas à l'heure de la compilation. Par conséquent, la récursion d'instanciation de modèle dans la clause
ele code> n'est jamais terminée.
NumberGeneration <1> Code> D'autre part est évalué à l'heure de la compilation et agit donc comme cas de terminaison de ce modèle récursif. P>
Je suis assez sûr que c'est spécifique au compilateur; Certains compilateurs peuvent essayer de générer les deux branches du mise à jour: ou comme Luc dit dans les commentaires, il se peut que le compilateur doit générer les deux branches , afin que le code échouera toujours. Je ne suis pas tout à fait sûr, c'est le cas, mais de toute façon, c'est une mauvaise idée de compter sur des conditions d'exécution pour contrôler la génération de code temporel. P> Il serait préférable d'utiliser la spécialisation: p> (ou vous pouvez raccourcir cela légèrement en se spécialisant pour aussi, sachez que certains compilateurs peuvent ne pas supporter des modèles profondément résinsifs; C ++ 03 suggère une profondeur minimale prise en charge de seulement 17, que C ++ 11 augmente à 1024. Vous devriez vérifier la limite de votre compilateur. P> p> si code> /
autre code> quelle que soit la valeur de
n code>, auquel cas la compilation échouera dans un événement. D'autres compilateurs peuvent évaluer la condition au moment de la compilation et générer uniquement du code de la branche exécuté, dans laquelle la compilation de cas réussira.
n = 0 code>, avec une fonction code> de code> qui ne fait rien) . p>
Hum, je pense que le compilateur doit I> instancier le modèle, même s'il pouvait détecter que le code ne sera jamais atteint au moment de l'exécution, je ne pense donc pas que ce code pourrait jamais terminer la compilation, quel que soit le compilateur que vous utilisez.
Vous n'avez pas besoin de spécialiser la structure entière, vous pouvez le faire pour la seule fonction.
@Paulmanta: En effet, vous pouvez. Merci.
Les modèles sont instanciés au moment de la compilation, le cas spécial de modèle empêche le compilateur de recouvrer ci-dessous 1 lors de la compilation. P>
Si-clauses sont évalués au moment de l'exécution, le compilateur a donc réussi à compiler votre code lorsqu'il aurait un effet. P>
Voici la bonne façon de le faire:
template<int N> struct NumberGeneration { static void out(std::ostream& os); }; template<int N> void NumberGeneration<N>::out(std::ostream& os) { NumberGeneration<N-1>::out(os); os << N << std::endl; } template<> void NumberGeneration<1>::out(std::ostream& os) { os << 1 << std::endl; } int main() { NumberGeneration<20>::out(std::cout); }