Par exemple, si j'ai ce code:
class SomeDataProcessor
{
public:
bool calc(const SomeData & d1, const SomeData & d2) const;
private:
//Some non-mutable, non-static member variables
}
SomeDataProcessor sdp;
SomeData data1;
SomeData data2;
someObscureFunction(sdp.calc(data1, data2),
sdp.calc(data1, data2));
Prenons le code équivalent potentiellement:
bool b = sdp.calc(data1, data2);
someObscureFunction(b,b);
Pour que cela soit valide, la fonction calc()
doit répondre à certaines exigences, et pour l'exemple j'appelle la propriété _pure_const_formula_
Un _pure_const_formula_
:
_pure_const_formula_
Par exemple, appeler un générateur de nombres aléatoires ne répondrait pas à ces exigences.
Le compilateur est-il autorisé à remplacer le premier code par le second, même s'il doit creuser récursivement dans les fonctions appelées? Les compilateurs modernes sont-ils capables de le faire?
GCC a le pure
attribut (utilisé comme __attribute__((pure))
) pour les fonctions qui indique au compilateur que les appels redondants peuvent être éliminés. Il est utilisé par exemple sur strlen
.
Je ne suis au courant d'aucun compilateur faisant cela automatiquement, surtout compte tenu du fait que les fonctions à appeler peuvent ne pas être disponibles sous forme source, et les formats de fichier objet ne contiennent aucune métadonnée sur le caractère pur ou non d'une fonction.
Oui, absolument.
Les compilateurs font cela tout le temps, et plus .
Par exemple, si toute votre fonction a été renvoyée true
et que sa définition était visible par le compilateur sur le site d'appel, l'appel de fonction entier serait probablement élidé, ce qui se traduirait par:
someObscureFunction(true, true);
Un programme pour lequel le compilateur a suffisamment d'informations peut être "optimisé" à partir d'une chaîne de tâches assez complexe jusqu'à peut-être une ou deux instructions. Maintenant, opérer sur les variables membres pousse l'optimiseur à sa limite dans une certaine mesure, mais si les variables sont private
, reçoivent une valeur initiale connue et ne sont mutées par aucune autre fonction membre, je ne le fais pas '' t voir pourquoi un compilateur ne pourrait pas simplement incorporer sa valeur connue s'il le voulait. Les compilateurs sont très, très intelligents.
Les gens pensent qu'un programme compilé est un mappage un à un des lignes de votre code source, mais ce n'est presque jamais vrai. Le but entier de C++ est qu'il s'agit d'une abstraction de ce que votre ordinateur va réellement faire quand il exécutera votre programme.
Non, étant donné le code affiché, le compilateur ne peut garantir que l'optimisation proposée n'aura aucune différence observable, et aucun compilateur moderne ne pourra optimiser loin le deuxième appel de fonction.
Un exemple très simple: cette méthode de classe peut utiliser un générateur de nombres aléatoires et enregistrer le résultat dans un tampon privé, qu'une autre partie du code lira plus tard. De toute évidence, l'élimination d'un appel de fonction entraîne désormais moins de valeurs générées de façon aléatoire placées dans ce tampon.
En d'autres termes, ce n'est pas parce qu'une méthode de classe est const
qu'elle n'a pas d'effets secondaires observables lorsqu'elle est appelée.
Non, le compilateur n'est pas autorisé à le faire dans ce cas. Le const
signifie uniquement que vous ne modifiez pas l'état de l'objet auquel la méthode appartient. Cependant, invoquer cette méthode plusieurs fois avec les mêmes paramètres d'entrée peut donner des résultats différents. Par exemple, pensez à une méthode qui produit un résultat aléatoire.
Oui, les compilateurs C modernes peuvent éliminer les appels de fonction redondants si et seulement si ils peuvent prouver qu'une telle optimisation se comporte comme -si la sémantique du programme d'origine a été suivie. Par exemple, cela signifie qu'ils pourraient éliminer plusieurs appels à la même fonction avec les mêmes arguments, si la fonction n'a pas d'effets secondaires et si sa valeur de retour dépend uniquement des arguments.
Maintenant, vous avez posé une question spécifique sur const
- c'est surtout utile au développeur, et non au codeur. Une fonction const
est un indice que la méthode ne modifie pas l'objet sur lequel elle est appelée, et const
les arguments sont des indices que les arguments ne sont pas modifiés. Cependant, la fonction peut (légalement1) supprime le const
- ness du pointeur this
ou de ses arguments. Le compilateur ne peut donc pas s'y fier.
De plus, même si les objets const
passés à une fonction n'ont jamais vraiment été modifiés dans cette fonction et que les fonctions const
n'ont jamais modifié l'objet récepteur, la méthode pourrait facilement s'appuyer sur des données globales mutables (et pourrait muter ces données). Considérons, par exemple, une fonction qui renvoie l'heure actuelle ou qui incrémente un compteur global.
Ainsi, les déclarations const
aident le programmeur, pas le compilateur2.
Cependant, le compilateur peut être en mesure d'utiliser d'autres astuces pour prouver que les appels sont redondants:
pure
et const
qui informent gcc
que les fonctions n'ont pas d'effets secondaires et dépendent uniquement de leurs paramètres (et sur les variables globales, dans le cas de pure
).1 Habituellement, tant que l'objet n'a pas été défini à l'origine comme const
.
2 Il y a un sens dans lequel const
définitions aident le compilateur: elles peuvent mettre des objets globaux définis comme const
dans une section en lecture seule de l'exécutable (si une telle fonctionnalité existe) et également combiner ces objets lorsqu'ils sont égaux (par exemple, des constantes de chaîne identiques).