web-dev-qa-db-fra.com

Longueur de calcul d'une chaîne C au moment de la compilation. Est-ce vraiment un constexpr?

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 ...

84
Felics

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:

  1. Utilisez-le là où une expression constante est requise, cela semblerait être n'importe où dans le projet de norme où la phrase shall be ... converted constant expression ou shall be ... constant expression est utilisé, tel qu'un tableau lié.
  2. Utilisez-le pour initialiser un constexpr comme je le décris ci-dessus.
68
Shafik Yaghmour

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;
}
25
Ben Voigt

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
16
Maxim Egorushkin

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 .

12
user2436830

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.

6
5gon12eder

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.

1
kaedinger