Introduction : l'éternel combat des constantes en C++
\n\nSi vous avez déjà programmé en C++ (ou même si vous commencez tout juste à l'apprendre), vous avez sûrement rencontré const. Ce préfixe magique promet que la valeur d'une variable ne changera pas. Mais ensuite est arrivé C++11, apportant avec lui constexpr. Et la confusion a commencé.
Sur Stack Overflow, la question "What's the difference between constexpr and const?" a recueilli des centaines de milliers de vues. Les programmeurs de tous niveaux confondent ces deux concepts, les utilisant comme des synonymes. Mais c'est une grave erreur !
\n\nDans ce tutoriel, nous allons une fois pour toutes comprendre ce qui différencie constexpr et const en C++. Vous apprendrez :
- \n
- Ce qu'est une constante de temps de compilation et une constante de temps d'exécution. \n
- Pourquoi
constn'est pas toujours synonyme de performance. \n - Comment
constexprtransforme le C++ en un langage moderne et sûr. \n - Quand utiliser chacune de ces constructions. \n
C'est parti ! 🚀
\n\nChapitre 1. const : une promesse, pas une garantie
\n\nCommençons par les bases. const en C++ est un qualificateur de type qui dit au compilateur : "Hé, cette valeur ne peut pas être modifiée après l'initialisation". Cela semble simple, mais il y a un hic.
Exemple 1 : const en action
\n\n#include <iostream>\n\nint main() {\n const int x = 42; // Promesse : x ne changera pas\n // x = 100; // Erreur de compilation — parfait !\n \n std::cout << x; // Affiche 42\n return 0;\n}\n\nTout est beau. Mais que se passe-t-il si la valeur d'une variable const n'est calculée qu'au moment de l'exécution ?
Exemple 2 : const calculé à l'exécution
\n\n#include <iostream>\n\nint get_value() {\n return 42; // La fonction retourne une valeur\n}\n\nint main() {\n const int y = get_value(); // Toujours const ?\n // Mais la valeur de y n'est connue qu'au moment de l'exécution !\n \n // Essayons de l'utiliser dans un tableau :\n // int arr[y]; // Erreur ! y n'est pas une constante de temps de compilation !\n \n return 0;\n}\n\nVoilà le principal piège ! const protège la variable contre les modifications, mais ne garantit pas que sa valeur est connue à l'étape de compilation. Si la valeur provient d'une fonction, elle n'est calculée qu'au lancement du programme.
Conclusion clé : const est un contrat d'immutabilité, pas de calculabilité au moment de la compilation.
Chapitre 2. constexpr : des constantes qui vivent dans le code
\n\nC'est ici qu'entre en scène constexpr. Ce spécificateur est apparu en C++11 et a radicalement changé l'approche des constantes. constexpr signifie : "Cette valeur doit être calculée au moment de la compilation". Ou, au moins, peut l'être.
Exemple 3 : variable constexpr
\n\n#include <iostream>\n\nint main() {\n constexpr int z = 42; // Garantie : la valeur est connue en compile-time\n \n int arr[z]; // Fonctionne ! z est une constante de temps de compilation\n \n std::cout << sizeof(arr) / sizeof(arr[0]); // Affiche 42\n return 0;\n}\n\nLa différence est évidente : constexpr permet d'utiliser la variable là où le compilateur a besoin de connaître la valeur exacte — par exemple, pour la taille d'un tableau ou des paramètres de template.
Exemple 4 : fonctions constexpr
\n\nMais le plus intéressant, ce sont les fonctions. constexpr peut être appliqué aux fonctions, et elles peuvent alors être calculées au moment de la compilation, si tous les arguments sont également des constantes de temps de compilation.
#include <iostream>\n\nconstexpr int square(int x) {\n return x * x;\n}\n\nint main() {\n constexpr int result = square(5); // Calculé en compile-time !\n int arr[result]; // Fonctionne\n \n std::cout << result; // 25\n \n int runtime_value =