web-dev-qa-db-fra.com

Pourquoi ** pas ** déclarer une fonction `constexpr`?

Toute fonction qui se compose uniquement d'une instruction de retour pourrait être déclarée constexpr et permettra donc d'être évaluée au moment de la compilation si tous les arguments sont constexpr et seules les fonctions constexpr sont appelées dans son corps. Y a-t-il une raison de ne pas déclarer any une telle fonction constexpr?

Exemple:

  constexpr int sum(int x, int y) { return x + y; }
  constexpr i = 10;
  static_assert(sum(i, 13) == 23, "sum correct");

Quelqu'un pourrait-il fournir un exemple où déclarer une fonction constexpr ferait du mal?


Quelques réflexions initiales:

Même s'il ne devrait y avoir aucune bonne raison de déclarer une fonction non constexpr je pourrais imaginer que le mot clé constexpr a un rôle transitoire: son absence dans le code qui n'a pas besoin d'évaluations à la compilation serait autoriser les compilateurs qui n'implémentent pas d'évaluations au moment de la compilation à compiler ce code (mais à échouer de manière fiable sur le code qui en a besoin, comme expliqué explicitement en utilisant constexpr).

Mais ce que je ne comprends pas: s'il n'y a pas de raison valable de déclarer une fonction non constexpr, pourquoi chaque fonction n'est pas dans la bibliothèque standard déclarée constexpr? (Vous ne pouvez pas affirmer que ce n'est pas encore fait parce qu'il n'y avait pas encore assez de temps pour le faire, parce que le faire pour tout est une évidence - contrairement à décider pour chaque fonction si pour le faire constexpr ou non.) --- Je suis conscient que N2976 n'exige délibérément pas de cstrs pour de nombreux types de bibliothèques standard tels que les conteneurs car cela serait trop limitant pour les implémentations possibles . Permet de les exclure de l'argument et de se demander: une fois qu'un type dans la bibliothèque standard a en fait un constexpr cstr, pourquoi toutes les fonctions qui opèrent dessus ne sont-elles pas déclarées constexpr?

Dans la plupart des cas, vous ne pouvez pas non plus affirmer que vous préférerez peut-être ne pas déclarer une fonction constexpr simplement parce que vous n'envisagez aucune utilisation au moment de la compilation: parce que si d'autres evtl. va utiliser votre code, ils peuvent voir une telle utilisation que vous ne voyez pas. (Mais accordé pour les types de traits de type et les choses similaires, bien sûr.)

Donc je suppose qu'il doit y avoir une bonne raison et un bon exemple pour ne pas déclarer délibérément une fonction constexpr?

(avec "chaque fonction", je veux toujours dire: chaque fonction qui remplit les conditions pour être constexpr, c'est-à-dire, est définie comme une instruction de retour unique, ne prend que des arguments de types avec des cstrs constexpr et appelle uniquement constexpr fonctions. Depuis C++ 14, beaucoup plus est autorisé dans le corps d'une telle fonction : par exemple, Les fonctions constexpr C++ 14 peuvent utiliser des variables et des boucles locales =, donc une classe de fonctions encore plus large pourrait être déclarée constexpr.)

La question Pourquoi std::forward discard constexpr- ness? est un cas particulier de celui-ci.

59
Lars

Les fonctions ne peuvent être déclarées constexpr que si elles respectent les règles de constexpr --- pas de transtypages dynamiques, pas d'allocation de mémoire, pas d'appels à des fonctions nonconstexpr, etc.

La déclaration d'une fonction dans la bibliothèque standard comme constexpr nécessite que TOUTES les implémentations respectent ces règles.

Tout d'abord, cela nécessite de vérifier pour chaque fonction qu'il peut être implémenté en tant que constexpr, ce qui est un travail long.

Deuxièmement, il s'agit d'une grande contrainte sur les implémentations et interdira de nombreuses implémentations de débogage. Cela ne vaut donc la peine que si les avantages l'emportent sur les coûts, ou si les exigences sont suffisamment strictes pour que la mise en œuvre doive de toute façon obéir aux règles constexpr. Faire cette évaluation pour chaque fonction est encore une fois un long travail.

34
Anthony Williams

Je pense que ce à quoi vous faites référence s'appelle évaluation partielle . Ce que vous touchez, c'est que certains programmes peuvent être divisés en deux parties - une pièce qui nécessite des informations d'exécution, et une pièce qui peut être effectuée sans aucune information d'exécution - et qu'en théorie, vous pouvez simplement évaluer pleinement la partie du programme qui n'a pas besoin d'informations d'exécution avant même de commencer à exécuter le programme. Il existe certains langages de programmation qui font cela. Par exemple, le langage de programmation D a un interpréteur intégré au compilateur qui vous permet d'exécuter du code au moment de la compilation, à condition qu'il respecte certaines restrictions.

Il y a quelques défis principaux pour faire fonctionner l'évaluation partielle. Tout d'abord, cela complique considérablement la logique du compilateur car le compilateur devra avoir la capacité de simuler toutes les opérations que vous pourriez mettre dans un programme exécutable au moment de la compilation. Dans le pire des cas, cela nécessite que vous ayez un interpréteur complet à l'intérieur du compilateur, ce qui pose un problème difficile (écrire un bon compilateur C++) et rend les ordres de grandeur plus difficiles à faire.

Je crois que la raison de la spécification actuelle sur constexpr est simplement de limiter la complexité des compilateurs. Les cas limités sont assez simples à vérifier. Il n'est pas nécessaire d'implémenter des boucles dans le compilateur (ce qui pourrait causer toute une série de problèmes, comme ce qui se passe si vous obtenez une boucle infinie à l'intérieur du compilateur). Cela évite également que le compilateur n'ait à évaluer les instructions susceptibles de provoquer des erreurs de segmentation lors de l'exécution, telles que suivre un mauvais pointeur.

Une autre considération à garder à l'esprit est que certaines fonctions ont des effets secondaires, comme la lecture depuis cin ou l'ouverture d'une connexion réseau. Des fonctions comme celles-ci ne peuvent fondamentalement pas être optimisées au moment de la compilation, car cela nécessiterait des connaissances uniquement disponibles au moment de l'exécution.

Pour résumer, il n'y a aucune raison théorique pour laquelle vous ne pourriez pas évaluer partiellement les programmes C++ au moment de la compilation. En fait, les gens font ça tout le temps. L'optimisation des compilateurs, par exemple, sont essentiellement des programmes qui essaient de faire cela autant que possible. La métaprogrammation de modèles est une instance où les programmeurs C++ essaient d'exécuter du code à l'intérieur du compilateur, et il est possible de faire de grandes choses avec les modèles en partie parce que les règles pour les modèles forment un langage fonctionnel, que le compilateur a plus de facilité à implémenter. De plus, si vous pensez au compromis entre les heures d'auteur du compilateur et les heures de programmation, la métaprogrammation de modèle montre que si vous êtes d'accord pour que les programmeurs se penchent en arrière pour obtenir ce qu'ils veulent, vous pouvez créer un langage assez faible (le système de modèle) et garder la complexité du langage simple. (Je dis "faible" comme dans "pas particulièrement expressif", pas "faible" au sens de la théorie de la calculabilité).

J'espère que cela t'aides!

14
templatetypedef

Si la fonction a des effets secondaires, vous ne voudriez pas la marquer constexpr. Exemple

Je ne peux pas obtenir de résultats inattendus, en fait, il semble que gcc 4.5.1 ignore simplement constexpr

3
Ben Voigt