9
votes

Compiler le temps de récursion et de conditionnels

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


0 commentaires

8 Réponses :


0
votes

C'est parce que les entiers peuvent être un code négatif et d'exécution (le si vérifier) ​​ne pas arrêter le compilateur instanciant du modèle avec 0, -1, -2, etc. Un compilateur pourrait Ê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.

En bref, tout comme tout la récursion, vous devez fournir votre propre cas de base.


0 commentaires

1
votes

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.

Parce que c'est la condition finale de récursif! Sans que cela puisse la fin récursive?


4 commentaires

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



12
votes

La génération de code et la compilation ne suprent pas en fonction des conditionnels! Considérons ceci: xxx

si vous ne déclarez jamais bar () , il s'agit d'une erreur de compilation même si la portée intérieure ne peut jamais être atteinte . Pour la même raison, NumberGeneration est toujours instancié, que cette branche puisse être atteinte ou non, et vous avez une récursion infinie.

En effet, la statique L'analogue des conditionnels est précisément spécialisation de modèles: xxx


3 commentaires

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 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.



4
votes

Le conditionnel si ne sera pas traité au moment de la compilation. Il sera traité à l'exécution.

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.


0 commentaires

1
votes

En général, la condition n == 1 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 n'est jamais terminée. NumberGeneration <1> D'autre part est évalué à l'heure de la compilation et agit donc comme cas de terminaison de ce modèle récursif.


0 commentaires

1
votes

Je suis assez sûr que c'est spécifique au compilateur; Certains compilateurs peuvent essayer de générer les deux branches du si / autre quelle que soit la valeur de n , 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.

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.

Il serait préférable d'utiliser la spécialisation: xxx

(ou vous pouvez raccourcir cela légèrement en se spécialisant pour n = 0 , avec une fonction de qui ne fait rien) .

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.


3 commentaires

Hum, je pense que le compilateur doit 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.



3
votes

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.

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.


0 commentaires

0
votes

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


0 commentaires