Continuons cette série sur les codes fantastiques avec les classes vides de C++
Quand on apprend que la taille (au sens de sizeof) d’une structure vide en C++ est de un octet, on est souvent un peu surpris. Puis on comprend que tout objet pouvant avoir une adresse, un objet ne peut avoir une taille vide (imaginez le bazar : un tableau de structure vide serait lui-même vide et tous les objets auraient la même adresse).
La plupart du temps, le compilateur arrive à « optimiser » le code associé, principalement quand il peut prouver que l’adresse de l’objet n’est jamais prise. Mais ce n’est pas le cas de la situation suivante :
Le code optimisé associé n’est pas extraordinaire, il comprend quelques manipulations de la pile :
Rien de trop surprenant si on y réfléchit à deux fois : le compilateur ne connaît rien de l’implémentation de empty::doit() qui peut très bien avoir pour effet de bord de stocker this dans une variable globale quelconque. Il faut donc laisser la possibilité à e d’avoir une adresse.
On peut très bien lui enlever cette possibilité en déclarant doit comme étant static. Mais dans ce cas-là, on pourra appeler cette méthode sans instance de l’objet, ce qui change le contrat avec l’utilisateur. Comment informer le compilateur que la méthode n’utilise pas l’adresse de l’objet ? Un ami m’a proposé cette élégante solution :
Le code optimisé par Clang pour ce bout de code est d’une absolue simplicité :
Le compilateur a supprimé l’appel de méthode pour directement invoquer la méthode statique, qui n’a par construction pas besoin de l’adresse de l’objet. Il devient inutile d’allouer l’objet sur la pile, et grâce au contrôle de la visibilité, l’interface publique de l’objet reste la même.
Cette optimisation peut paraître ridicule, mais elle permet de supprimer quelques manipulations de la pile, et épargne un store supplémentaire quand on active -ftrivial-auto-var-init=pattern, ce qui lui a valu d’atterrir dans Firefox avec https://phabricator.services.mozilla.com/D184083.