J'essaie de calculer la longueur d'un littéral de chaîne au moment de la compilation. Pour ce faire, j'utilise le code suivant:
#include <cstdio>
int constexpr length(const char* str)
{
return *str ? 1 + length(str + 1) : 0;
}
int main()
{
printf("%d %d", length("abcd"), length("abcdefgh"));
}
Tout fonctionne comme prévu, le programme en imprime 4 et 8. Le code d'assemblage généré par clang montre que les résultats sont calculés au moment de la compilation:
0x100000f5e: leaq 0x35(%rip), %rdi ; "%d %d"
0x100000f65: movl $0x4, %esi
0x100000f6a: movl $0x8, %edx
0x100000f6f: xorl %eax, %eax
0x100000f71: callq 0x100000f7a ; symbol stub for: printf
Ma question: est-il garanti par le standard que la fonction length
sera évaluée lors de la compilation?
Si cela est vrai, la porte pour les calculs de littéraux de chaîne de temps de compilation vient de s'ouvrir pour moi ... par exemple, je peux calculer des hachages au moment de la compilation et bien plus encore ...
Il n’est pas garanti que les expressions constantes soient évaluées au moment de la compilation, nous avons uniquement une citation non normative de projet de norme C++ section 5.19
Expressions constantes qui dit ceci cependant:
[...]> [Note: les expressions constantes peuvent être évaluées pendant la traduction. — note de fin]
Vous pouvez assigner le résultat à la variable constexpr
pour être sûr qu’il est évalué au moment de la compilation, nous pouvons le voir à partir de référence C++ 11 de Bjarne Stroustrup qui indique ( emphasis le mien):
En plus de pouvoir évaluer des expressions au moment de la compilation, nous voulons pouvoir demander que les expressions soient évaluées au moment de la compilation; constexpr devant une définition de variable fait que (et implique const):
Par exemple:
constexpr int len1 = length("abcd") ;
Bjarne Stroustrup résume le moment où nous pouvons assurer l’évaluation du temps de compilation dans ce entrée de blog isocpp et dit:
[...] La réponse correcte - comme le dit Herb - est que, conformément à la norme, une fonction constexpr peut être évaluée au moment du compilateur ou de l'exécution, à moins qu'elle ne soit utilisée comme expression constante. Dans ce cas, elle doit être évaluée à la compilation. -temps. Pour garantir l’évaluation au moment de la compilation, nous devons soit l’utiliser là où une expression constante est requise (par exemple, en tant que tableau ou comme étiquette de casse), soit l’utiliser pour initialiser un constexpr. J'espérerais qu'aucun compilateur qui se respecte ne manquerait l'occasion d'optimisation pour faire ce que j'avais dit au début: "Une fonction constexpr est évaluée au moment de la compilation si tous ses arguments sont des expressions constantes."
Donc, cela décrit deux cas où il devrait être évalué au moment de la compilation:
shall be ... converted constant expression
ou shall be ... constant expression
est utilisé, tel qu'un tableau lié.constexpr
comme je le décris ci-dessus.Il est très facile de savoir si un appel à une fonction constexpr
aboutit à un expression constante fondamentale ou s'il est simplement optimisé:
Utilisez-le dans un contexte où une expression constante est requise.
int main()
{
constexpr int test_const = length("abcd");
std::array<char,length("abcdefgh")> test_const2;
}
Juste une remarque, que les compilateurs modernes (comme gcc-4.x) font strlen
pour les littéraux de chaîne au moment de la compilation car il est normalement défini comme fonction intrinsèque . Sans optimisations activées. Bien que le résultat ne soit pas une constante de temps de compilation.
Par exemple.:
printf("%zu\n", strlen("abc"));
Résulte en:
movl $3, %esi # strlen("abc")
movl $.LC0, %edi # "%zu\n"
movl $0, %eax
call printf
Permettez-moi de vous proposer une autre fonction qui calcule la longueur d'une chaîne au moment de la compilation sans être récursive.
template< size_t N >
constexpr size_t length( char const (&)[N] )
{
return N-1;
}
Jetez un oeil à ceci exemple de code chez ideone .
Il n'y a aucune garantie qu'une fonction constexpr
soit évaluée au moment de la compilation, bien qu'un compilateur raisonnable le fasse aux niveaux d'optimisation appropriés activés. D'autre part, les paramètres de modèle doivent être évalués à la compilation.
J'ai utilisé le truc suivant pour forcer l'évaluation au moment de la compilation. Malheureusement, cela ne fonctionne qu'avec des valeurs intégrales (c'est-à-dire pas avec des valeurs à virgule flottante).
template<typename T, T V>
struct static_eval
{
static constexpr T value = V;
};
Maintenant, si vous écrivez
if (static_eval<int, length("hello, world")>::value > 7) { ... }
vous pouvez être sûr que l'instruction if
est une constante de compilation sans temps système.
Une brève explication de l'entrée de Wikipedia sur expressions constantes généralisées :
L'utilisation de constexpr sur une fonction impose certaines limitations à ce que cette fonction peut faire. Premièrement, la fonction doit avoir un type de retour non nul. Deuxièmement, le corps de la fonction ne peut pas déclarer de variables ni définir de nouveaux types. Troisièmement, le corps ne peut contenir que des déclarations, des instructions null et une seule instruction return. Il doit exister des valeurs d'argument telles que, après la substitution d'argument, l'expression de l'instruction return génère une expression constante.
Avoir le mot-clé constexpr
avant une définition de fonction demande au compilateur de vérifier si ces limitations sont respectées. Si la réponse est oui et que la fonction est appelée avec une constante, la valeur renvoyée est garantie comme étant constante et peut donc être utilisée partout où une expression constante est requise.