web-dev-qa-db-fra.com

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

É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 fonction constexpr, ce qui me permet de l'utiliser lors de la définition des variables constexpr, 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 contexte nonconstexpr, et elle peut désormais é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? Comme, en faisant cette fonction constexpr, puis-je jamais rencontrer une situation où cette fonction ne sera plus utilisable dans un circonstance particulière, ou où il se comportera mal?

26
Xirema

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 qualificatif constexpr 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 constexpr pour les 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 que 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, c'est moins un problème puisque 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 de l'obscurcissement.

19
amon

Marquer une fonction comme constexpr en fait é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 fonctions constexpr 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 uniquement l'en-tête, puis des liens vers le fichier objet de cette source) constexpr ne fonctionnera 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 fonction constexpr est également limitée à certains égards, donc pour certaines fonctions, il peut ne pas être une option du tout. Les restrictions comprennent:

  1. fonctions virtuelles ne peut pas ê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 un bloc try.
  5. il ne peut pas contenir une définition 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 une instruction goto ou asm), mais vous avez l'idée - pour pas mal de choses, ça ne marchera tout simplement pas.

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

12
Jerry Coffin