Ce texte est devenu quelque peu caduque depuis l'avènement des expressions λ avec C++ 11, mais je le laisse ici à titre de référence historique
Certains langages, par exemple Modula II ou Pascal, permettent à une fonction de contenir une autre fonction, donc de construire des fonctions imbriquées.
Ceci facilite certaines stratégies comme utiliser une fonction comme devanture de validation (incluant du traitement d'exceptions, chose très coûteuse) puis une autre pour des opérations à « haute performance ». On retrouve d'ailleurs le même concept en utilisant des méthodes privées à haute performance et publiques à haute sécurité dans les langages OO.
Les langages C et C++ ont choisi, quant à eux, de ne pas permettre l'imbrication de fonctions en tant que tel. Ce concept peut toutefois être simulé dans les langages en appliquant certaines stratégies de programmation.
//
// Fonction qu'on voudra cacher (peut-être même dans
// puissance()) et qui présume que Base != 0
//
long puissance_danger(int base, int exp)
{
long resultat;
if (exp == 0)
resultat = 1;
else if (exp % 2 == 0)
{
resultat = puissance_danger(base, exp / 2);
resultat *= resultat;
}
else
resultat = base * puissance_danger(base, exp - 1);
return resultat;
}
class PasUnNombre {};
//
// Fonction accessible à tous et (un peu) sécurisée
//
long puissance(int base, int exp)
{
if (base == 0 && exp == 0)
throw PasUnNombre{};
return Base==0 ? 0 : puissance_danger(base, exp);
}
Traditionnellement, on pensera à une fonction sécuritaire dont le prototype est exposé dans un fichier d'en-tête accessible à tous, qui validera les intrants puis déléguera le véritable travail à une autre fonction, cachée (déclarée static ou dans un espace nommé anonyme dans un fichier source) qui, elle, présumera que ses intrants sont valides au préalable et ira aussi rapidement que possible.
En C++, il est possible de déclarer une classe locale à une fonction, et il est possible de faire d'une telle classe un foncteur. Ce faisant, on peut pratiquement y concevoir des fonctions imbriquées (fonctions locales à d'autres fonctions).
Le code proposé ci-dessous montre comment on pourrait y arriver.
class PasUnNombre {};
long puissance(int base, int exp)
{
struct Interne
{
long operator()(int base, int exp) const
{
long resultat;
if (exp == 0)
resultat = 1;
else if (exp % 2 == 0)
{
resultat = operator()(base, exp / 2);
resultat *= resultat;
}
else
resultat = base * operator()(base, exp - 1);
return resultat;
}
};
if (base == 0 && exp == 0)
throw PasUnNombre{};
return base==0 ? 0 : Interne{}(base, exp);
}
Le foncteur PuissanceLocal est, comme son nom l'indique, déclaré localement à la fonction Puissance(). Il y est instancié au besoin pour réaliser la tâche normalement demandée d'une fonction locale.
J'ai utilisé un foncteur ici par souci de conformité avec la notation traditionnelle de fonction mais n'importe quelle méthode d'instance aurait fait l'affaire (une classe locale n'est pas autorisée à exposer de méthodes de classe).
Aussi, nous ne profitons pas pleinement ici du caractère OO d'un foncteur, alors la version utilisant un foncteur local peut être raffinée et gagner en performance.
En particulier, constatez que la valeur du paramètre Base est la même pour toutes les invocations et qu'elle pourrait être fixée à la construction du foncteur pour réduire le nombre de paramètres à passer lors de chaque invocation de l'opérateur () responsable ici du véritable calcul.
Une version raffinée de cette solution, tirant profit à la fois du caractère local du foncteur et de son caractère OO, pourrait être celle proposée ci-dessous :
class PasUnNombre {};
long Puissance(int base, int exp)
{
struct Interne
{
int base;
Interne(int base) noexcept
: base{base}
{
}
long operator()(int exp)
{
long resultat;
if (exp == 0)
resultat = 1;
else if (exp % 2 == 0)
{
resultat = operator()(exp / 2);
resultat *= resultat;
}
else
resultat = base * operator()(exp - 1);
return resultat;
}
};
if (base == 0 && exp == 0)
throw PasUnNombre{};
return base==0 ? 0 : Interne{base}(Exp);
}
D'autres raffinements sont posibles mais je vous laisse vous amuser.