Est-il jamais mauvais de marquer une fonction C ++ constexpr?

26

Étant donné une fonction très triviale,

int transform(int val) {
    return (val + 7) / 8;
}

Il devrait être très évident qu'il est facile de transformer cette fonction en constexprfonction, ce qui me permet de l'utiliser lors de la définition de constexprvariables, comme ceci:

constexpr int transform(int val) {
    return (val + 7) / 8;
}

Mon hypothèse est qu'il s'agit strictement d'une amélioration, car la fonction peut toujours être appelée dans un non- constexprcontexte, et elle peut maintenant également être utilisée pour aider à définir des variables constantes au moment de la compilation.

Ma question est, y a-t-il des situations où c'est une mauvaise idée? Par exemple, en faisant cette fonction constexpr, puis-je jamais rencontrer une situation où cette fonction ne sera plus utilisable dans une circonstance particulière, ou où elle se comportera mal?

Xirema
la source
1
La seule chose à laquelle je pouvais penser était les bogues du compilateur. Il est possible qu'un appel de fonction constexpr récursif puisse provoquer une étape de compilation très lente ou même un blocage de mémoire par le compilateur.
Zan Lynx

Réponses:

19

Cela n'a d'importance que si la fonction fait partie d'une interface publique et que vous souhaitez conserver les futures versions de votre API compatibles binaires. Dans ce cas, vous devez réfléchir soigneusement à la manière dont vous souhaitez faire évoluer votre API et à l'endroit où vous avez besoin de points d'extension pour les modifications futures.

Cela fait d'un constexprqualificatif une décision de conception irrévocable. Vous ne pouvez pas supprimer ce qualificatif sans une modification incompatible de votre API. Cela limite également la façon dont vous pouvez implémenter cette fonction, par exemple, vous ne pourrez pas effectuer de journalisation dans cette fonction. Toutes les fonctions triviales ne resteront pas triviales dans l'éternité.

Cela signifie que vous devriez utiliser de préférence constexprdes fonctions qui sont intrinsèquement des fonctions pures et qui seraient réellement utiles au moment de la compilation (par exemple pour la métaprogrammation de modèle). Il ne serait pas bon de rendre les fonctions constexpr simplement parce que l'implémentation actuelle se trouve être constexpr-able.

Lorsque l'évaluation à la compilation n'est pas nécessaire, l'utilisation de fonctions en ligne ou de fonctions avec liaison interne semble plus appropriée constexpr. Toutes ces variantes ont en commun le fait que le corps de fonction est «public» et est disponible dans la même unité de compilation que l'emplacement d'appel.

Si la fonction en question ne fait pas partie d'une API publique stable, cela pose moins de problème car vous pouvez modifier arbitrairement la conception à volonté. Mais puisque vous contrôlez maintenant tous les sites d'appels, il n'est pas nécessaire de marquer une fonction constexpr «juste au cas où». Vous savez si vous utilisez cette fonction dans un contexte constexpr. L'ajout de qualificatifs inutilement restrictifs pourrait alors être considéré comme une obfuscation.

amon
la source
12

Marquer une fonction comme en constexprfait également une fonction en ligne § [dcl.constexpr] / 1:

Une fonction ou un membre de données statiques déclaré avec le spécificateur constexpr est implicitement une fonction ou une variable en ligne (7.1.6).

inline, à son tour, signifie que vous devez inclure la définition de cette fonction dans chaque unité de traduction dans laquelle elle peut être utilisée. Cela signifie essentiellement que les constexprfonctions doivent être soit:

  1. limité à une utilisation dans une unité de traduction, ou
  2. défini dans un en-tête.

La plupart des fonctions typiques que vous souhaitez déclarer dans un en-tête et définir dans un fichier source (et tout autre élément qui les utilise inclut simplement l'en-tête, puis les liens avec le fichier objet de cette source) constexprne fonctionneront tout simplement pas.

En théorie, je suppose que vous pouvez simplement tout déplacer dans les en-têtes et n'avoir qu'un seul fichier source qui inclut uniquement tous les en-têtes, mais cela nuirait considérablement aux temps de compilation, et pour la plupart des projets sérieux, il faudrait d'immenses quantités de mémoire pour compiler.

Une constexprfonction est également limitée à certains égards, donc pour certaines fonctions, il peut ne pas être une option du tout. Les restrictions comprennent:

  1. les fonctions virtuelles ne peuvent pas l' être constexpr.
  2. son type de retour doit être un "type littéral" (par exemple, aucun objet avec des ctors ou des dtors non-trivalents).
  3. tous ses paramètres doivent être des types littéraux.
  4. le corps de la fonction ne peut pas contenir de trybloc.
  5. il ne peut pas contenir de définition de variable d'un type non littéral, ou quoi que ce soit avec une durée de stockage statique ou de thread.

J'ai sauté quelques choses plutôt obscures (par exemple, il ne peut pas non plus contenir de gotoou une asmdéclaration), mais vous avez l'idée - pour pas mal de choses, cela ne fonctionnera tout simplement pas.

Conclusion: oui, il y a pas mal de situations où ce serait une mauvaise idée.

Jerry Coffin
la source
"il ne doit pas être virtuel (jusqu'à C ++ 20)" Je me demande comment une fonction virtuelle peut être constexpr? Que font les compilateurs?
chaosink