Quand devez-vous utiliser la capacité constexpr en C ++ 11?

337

Il me semble qu'avoir une "fonction qui renvoie toujours 5" brise ou dilue le sens de "appeler une fonction". Il doit y avoir une raison, ou un besoin pour cette capacité, sinon ce ne serait pas en C ++ 11. Pourquoi est-il là?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

Il me semble que si j'écrivais une fonction qui retourne une valeur littérale et que j'arrivais à une révision de code, quelqu'un me dirait, je devrais alors, déclarer une valeur constante au lieu d'écrire return 5.

Warren P
la source
28
Pouvez-vous définir une fonction récursive qui renvoie un constexpr? Si oui, je peux voir une utilisation.
avant le
20
Je crois que la question devrait indiquer "pourquoi introduire un nouveau mot-clé (!) Si le compilateur peut déduire pour lui-même si une fonction peut être évaluée en temps de compilation ou non". L'avoir "garanti par un mot-clé" sonne bien, mais je pense que je préférerais l'avoir garanti chaque fois que c'est possible, sans avoir besoin d'un mot-clé.
Kos
6
@Kos: Quelqu'un qui est PLUS familier avec les internes C ++ préférerait probablement votre question, mais ma question vient du point de vue d'une personne qui a déjà écrit du code C, mais qui ne connaît pas du tout les mots-clés C ++ 2011, ni les détails d'implémentation du compilateur C ++ . Pouvoir raisonner sur l'optimisation du compilateur et la déduction d'expression constante est un sujet pour une question d'utilisateur plus avancé que celle-ci.
Warren P
8
@Kos Je pensais dans le même sens que vous, et la réponse que je suis venu avec était sans constexpr, comment voulez - vous (facilement) savoir que le compilateur fait la compilation-évalué la fonction pour vous? Je suppose que vous pouvez vérifier la sortie de l'assembly pour voir ce qu'elle a fait, mais il est plus facile de dire au compilateur que vous avez besoin de cette optimisation, et si pour une raison quelconque il ne peut pas le faire pour vous, cela vous donnera une belle compilation- erreur au lieu de ne pas réussir à optimiser silencieusement l’endroit où vous vous attendiez.
Jeremy Friesner
3
@Kos: Vous pourriez dire la même chose const. En fait, l' intention prescrite est utile ! Les dimensions des tableaux sont l'exemple canonique.
Courses de légèreté en orbite le

Réponses:

303

Supposons qu'il fasse quelque chose d'un peu plus compliqué.

constexpr int MeaningOfLife ( int a, int b ) { return a * b; }

const int meaningOfLife = MeaningOfLife( 6, 7 );

Vous avez maintenant quelque chose qui peut être évalué à une constante tout en conservant une bonne lisibilité et en permettant un traitement légèrement plus complexe que de simplement définir une constante sur un nombre.

Il fournit essentiellement une bonne aide à la maintenabilité car il devient plus évident de savoir ce que vous faites. Prenons max( a, b )par exemple:

template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }

C'est un choix assez simple, mais cela signifie que si vous appelez maxavec des valeurs constantes, il est explicitement calculé au moment de la compilation et non au moment de l'exécution.

Un autre bon exemple serait une DegreesToRadiansfonction. Tout le monde trouve les degrés plus faciles à lire que les radians. Bien que vous sachiez que 180 degrés sont en radians, il est beaucoup plus clair comme suit:

const float oneeighty = DegreesToRadians( 180.0f );

Beaucoup de bonnes informations ici:

http://en.cppreference.com/w/cpp/language/constexpr

Goz
la source
18
Excellent point en disant au compilateur d'essayer de calculer la valeur au moment de la compilation. Je suis curieux de savoir pourquoi const ne fournit pas cette fonctionnalité lorsque des optimisations spécifiques sont spécifiées? Ou alors?
TamusJRoyce
11
@Tamus: Souvent, ce n'est pas obligatoire. constexpr oblige le compilateur et crachera une erreur s'il ne le peut pas.
Goz
20
Je le vois maintenant. Le péché (0,5) en est un autre. Cela remplace proprement les macros C.
Warren P
10
Je peux voir cela comme une nouvelle question d'entrevue: Expliquez les différences entre le mot clé const et constexpr.
Warren P
2
Afin de documenter ce point pour moi-même, j'ai écrit un code similaire comme ci-dessus et encore avec la fonction étant "const" plutôt que "constexpr". Comme j'utilise Clang3.3, -pedantic-errors et -std = c ++ 11, je m'attendais à ce que ce dernier ne compile pas. Il a compilé et exécuté comme dans le cas "constexpr". Supposez-vous qu'il s'agit d'une extension clang ou y a-t-il eu un ajustement à la spécification C ++ 11 depuis la réponse à ce message?
Arbalest
144

introduction

constexprn'a pas été présenté comme un moyen de dire à l'implémentation que quelque chose peut être évalué dans un contexte qui nécessite une expression constante ; les implémentations conformes ont pu le prouver avant C ++ 11.

Quelque chose qu'une implémentation ne peut pas prouver, c'est l' intention d'un certain morceau de code:

  • Qu'est-ce que le développeur veut exprimer avec cette entité?
  • Faut-il autoriser aveuglément le code à être utilisé dans une expression constante , simplement parce que cela fonctionne?

De quoi le monde serait-il privé constexpr?

Disons que vous développez une bibliothèque et réalisez que vous voulez pouvoir calculer la somme de chaque entier de l'intervalle (0,N].

int f (int n) {
  return n > 0 ? n + f (n-1) : n;
}

Le manque d'intention

Un compilateur peut facilement prouver que la fonction ci-dessus peut être appelée dans une expression constante si l'argument passé est connu pendant la traduction; mais vous n'avez pas déclaré cela comme une intention - il se trouve que c'est le cas.

Maintenant, quelqu'un d'autre arrive, lit votre fonction, fait la même analyse que le compilateur; " Oh, cette fonction est utilisable dans une expression constante!" et écrit le code suivant.

T arr[f(10)]; // freakin' magic

L'optimisation

En tant que développeur de bibliothèque "génial" , vous décidez que fle résultat devrait être mis en cache lors de son appel; qui voudrait calculer le même ensemble de valeurs encore et encore?

int func (int n) { 
  static std::map<int, int> _cached;

  if (_cached.find (n) == _cached.end ()) 
    _cached[n] = n > 0 ? n + func (n-1) : n;

  return _cached[n];
}

Le résultat

En introduisant votre optimisation idiote, vous venez de rompre chaque utilisation de votre fonction qui se trouvait dans un contexte où une expression constante était requise.

Vous n'avez jamais promis que la fonction était utilisable dans une expression constante , et sans quoi constexpril n'y aurait aucun moyen de fournir une telle promesse.


Alors, pourquoi avons-nous besoin constexpr?

L'utilisation principale de constexpr est de déclarer l' intention .

Si une entité n'est pas marquée comme constexpr- elle n'a jamais été destinée à être utilisée dans une expression constante ; et même si c'est le cas, nous comptons sur le compilateur pour diagnostiquer un tel contexte (car il ne tient pas compte de notre intention).

Filip Roséen - refp
la source
25
C'est probablement la bonne réponse, car les récents changements dans C ++ 14 et C ++ 17 permettent d'utiliser une gamme beaucoup plus large du langage dans les constexprexpressions. En d'autres termes, à peu près tout peut être annoté constexpr(peut-être qu'un jour, il disparaîtra simplement à cause de cela?), Et à moins que l'on ait un critère de moment à utiliser constexprou non, presque tout le code sera écrit comme tel .
alecov
4
@alecov Certainement pas tout ... I/O, syscallet dynamic memory allocationne peut certainement pas être marqué comme constexprEn outre, tout ne devrait pas l'être constexpr.
JiaHao Xu
1
@alecov Certaines fonctions sont destinées à être exécutées au moment de l'exécution et cela n'a aucun sens de le faire au moment de la compilation.
JiaHao Xu
1
J'aime aussi mieux cette réponse. L'évaluation du temps de compilation est une optimisation soignée, mais ce que vous obtenez vraiment constexprest la garantie d'une sorte de comportement. Tout comme le constfait.
Tomáš Zato - Réintègre Monica
Quel compilateur permet à cette version sans constexpr de int f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)]; ne pas pouvoir compiler ça n'importe où?
Jer
91

Prenez std::numeric_limits<T>::max(): pour une raison quelconque, c'est une méthode. constexprserait bénéfique ici.

Un autre exemple: vous voulez déclarer un C-array (ou a std::array) qui est aussi grand qu'un autre tableau. La façon de procéder pour le moment est la suivante:

int x[10];
int y[sizeof x / sizeof x[0]];

Mais ne serait-il pas préférable de pouvoir écrire:

int y[size_of(x)];

Grâce à constexpr, vous pouvez:

template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
    return N;
}
Konrad Rudolph
la source
1
+1 pour une belle utilisation du modèle, mais cela fonctionnerait exactement de la même manière sans constexpr, n'est-ce pas?
Kos
21
@Kos: Non. Il retournerait une valeur d'exécution. constexprforce le compliant à faire en sorte que la fonction renvoie une valeur au moment de la compilation (si elle le peut).
deft_code
14
@Kos: sans le, constexpril ne peut pas être utilisé dans une déclaration de taille de tableau, ni comme argument de modèle, que le résultat de l'appel de fonction soit ou non une constante de compilation. Ces deux sont fondamentalement les seuls cas d'utilisation, constexprmais au moins le cas d'utilisation de l'argument modèle est assez important.
Konrad Rudolph
2
"pour une raison quelconque, c'est une méthode": la raison en est qu'il n'y a que des entiers de temps de compilation en C ++ 03, mais aucun autre type de temps de compilation, donc seule une méthode peut fonctionner pour des types arbitraires avant C ++ 11.
Sebastian Mach
5
@LwCui Non, ce n'est pas "ok": GCC est juste laxiste par défaut sur certaines choses. Utilisez l' -pedanticoption et elle sera signalée comme une erreur.
Konrad Rudolph
19

constexprles fonctions sont vraiment sympa et un excellent ajout à c ++. Cependant, vous avez raison en ce que la plupart des problèmes qu'il résout peuvent être contournés de manière non élégante avec les macros.

Cependant, l'une des utilisations de constexprn'a pas de constantes typées C ++ 03 équivalentes.

// This is bad for obvious reasons.
#define ONE 1;

// This works most of the time but isn't fully typed.
enum { TWO = 2 };

// This doesn't compile
enum { pi = 3.1415f };

// This is a file local lvalue masquerading as a global
// rvalue.  It works most of the time.  But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;

// This is a true constant rvalue
constexpr float pi = 3.1415f;

// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor

struct A
{
   static const int four = 4;
   static const int five = 5;
   constexpr int six = 6;
};

int main()
{
   &A::four; // linker error
   &A::six; // compiler error

   // EXTREMELY subtle linker error
   int i = rand()? A::four: A::five;
   // It not safe use static const class variables with the ternary operator!
}

//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;
deft_code
la source
12
Pourriez-vous s'il vous plaît clarifier cette «erreur de l'éditeur de liens EXTRÊMEMENT subtile»? Ou au moins fournir un pointeur vers une clarification?
enobayram
4
@enobayram, L'opérateur ternaire prend l'adresse des opérandes. Ce n'est pas évident d'après le code. Tout se compile bien, mais le lien échoue car l'adresse de fourne se résout pas. J'ai dû vraiment creuser pour savoir qui prenait l'adresse de ma static constvariable.
deft_code
23
"C'est mauvais pour des raisons évidentes": la raison la plus évidente étant le point-virgule, non?
TonyK
4
L'erreur "EXTRÊMEMENT subtile de l'éditeur de liens" m'a complètement perplexe. Ni fourni fivesont dans la portée.
Steven Lu
3
voir aussi le nouveau enum classtype, il corrige certains des problèmes d'énumération.
ninMonkey
14

D'après ce que j'ai lu, le besoin de constexpr provient d'un problème de métaprogrammation. Les classes de traits peuvent avoir des constantes représentées comme des fonctions, pensez: numeric_limits :: max (). Avec constexpr, ces types de fonctions peuvent être utilisés dans la métaprogrammation, ou comme limites de tableau, etc., etc.

Un autre exemple hors de ma tête serait que pour les interfaces de classe, vous voudrez peut-être que les types dérivés définissent leurs propres constantes pour certaines opérations.

Éditer:

Après farfouillé sur le SO, il semble que d' autres sont venus avec quelques exemples de ce qui pourrait être possible avec constexprs.

luke
la source
"Pour faire partie d'une interface, il faut être une fonction"?
Daniel Earwicker
Maintenant que je peux voir l'utilité de cela, je suis un peu plus excité à propos de C ++ 0x. Cela semble une chose bien pensée. Je savais qu'ils devaient l'être. Ces uber-geeks standard de langue font rarement des choses aléatoires.
Warren P
Je suis beaucoup plus enthousiasmé par lambdas, le modèle de thread, initializer_list, les références rvalue, les modèles variadic, les nouvelles surcharges de liaison ... il y a beaucoup à attendre.
luke
1
Oh oui, mais je comprends déjà les lambdas / fermetures dans plusieurs autres languges. constexprest plus spécifiquement utile dans un compilateur doté d'un puissant système d'évaluation des expressions à la compilation. C ++ n'a vraiment aucun homologue dans ce domaine. (c'est un éloge fort pour C ++ 11, à mon humble avis)
Warren P
11

Extrait du discours de Stroustrup à "Going Native 2012":

template<int M, int K, int S> struct Unit { // a unit in the MKS system
       enum { m=M, kg=K, s=S };
};

template<typename Unit> // a magnitude with a unit 
struct Value {
       double val;   // the magnitude 
       explicit Value(double d) : val(d) {} // construct a Value from a double 
};

using Speed = Value<Unit<1,0,-1>>;  // meters/second type
using Acceleration = Value<Unit<1,0,-2>>;  // meters/second/second type
using Second = Unit<0,0,1>;  // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second 

constexpr Value<Second> operator"" s(long double d)
   // a f-p literal suffixed by ‘s’
{
  return Value<Second> (d);  
}   

constexpr Value<Second2> operator"" s2(long double d)
  // a f-p literal  suffixed by ‘s2’ 
{
  return Value<Second2> (d); 
}

Speed sp1 = 100m/9.8s; // very fast for a human 
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)  
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit) 
Acceleration acc = sp1/0.5s; // too fast for a human

la source
2
Cet exemple se trouve également dans l'article de Software Development for Infrastructure de Stroustrup .
Matthieu Poullet
clang-3.3: erreur: le type de retour de la fonction constexpr 'Value <Second>' n'est pas un type littéral
Mitja
C'est bien mais qui met des littéraux dans un code comme celui-ci. Il serait logique que votre compilateur "vérifie vos unités" si vous écriviez une calculatrice interactive.
bobobobo
5
@bobobobo ou si vous écriviez un logiciel de navigation pour Mars Climate Orbiter, peut-être :)
Jeremy Friesner
1
Pour le compiler - 1. Utilisez le soulignement dans les suffixes littéraux. 2. ajoutez l'opérateur "" _m pour 100_m. 3. utilisez 100.0_m ou ajoutez une surcharge qui accepte les longs non signés. 4. Déclarez le constructeur Value constexpr. 5. Ajoutez l'opérateur correspondant / à la classe Value comme ceci: constexpr auto operator / (const Value <Y> & other) const {return Value <Unit <TheUnit :: m - Value <Y> :: TheUnit :: m, TheUnit :: kg - Valeur <Y> :: TheUnit :: kg, TheUnit :: s - Valeur <Y> :: TheUnit :: s >> (val / other.val); }. Où TheUnit est typedef pour Unit ajouté à l'intérieur de la classe Value.
0kcats
8

Une autre utilisation (non encore mentionnée) est celle des constexprconstructeurs. Cela permet de créer des constantes de temps de compilation qui n'ont pas besoin d'être initialisées pendant l'exécution.

const std::complex<double> meaning_of_imagination(0, 42); 

Associez cela à des littéraux définis par l'utilisateur et vous avez une prise en charge complète des classes définies par l'utilisateur littérales.

3.14D + 42_i;
Motti
la source
6

Il y avait un schéma avec la métaprogrammation:

template<unsigned T>
struct Fact {
    enum Enum {
        VALUE = Fact<T-1>*T;
    };
};

template<>
struct Fact<1u> {
    enum Enum {
        VALUE = 1;
    };
};

// Fact<10>::VALUE is known be a compile-time constant

Je crois qu'il a constexprété introduit pour vous permettre d'écrire de telles constructions sans avoir besoin de modèles et de constructions étranges avec spécialisation, SFINAE et d'autres choses - mais exactement comme vous écririez une fonction d'exécution, mais avec la garantie que le résultat sera déterminé lors de la compilation -temps.

Cependant, notez que:

int fact(unsigned n) {
    if (n==1) return 1;
    return fact(n-1)*n;
}

int main() {
    return fact(10);
}

Compilez ceci avec g++ -O3et vous verrez que cela fact(10)est en effet évalué au moment de la compilation!

Un compilateur compatible VLA (donc un compilateur C en mode C99 ou un compilateur C ++ avec des extensions C99) peut même vous permettre de faire:

int main() {
    int tab[fact(10)];
    int tab2[std::max(20,30)];
}

Mais que ce soit du C ++ non standard pour le moment - constexprressemble à un moyen de lutter contre cela (même sans VLA, dans le cas ci-dessus). Et il y a toujours le problème de la nécessité d'avoir des expressions constantes "formelles" comme arguments de modèle.

Kos
la source
La fonction de fait n'est pas évaluée au moment de la compilation. Il doit être constexpr et ne doit avoir qu'une seule instruction de retour.
Sumant
1
@Sumant: Vous avez raison de dire qu'il n'a pas besoin d'être évalué au moment de la compilation, mais c'est le cas! Je faisais allusion à ce qui se passe vraiment dans les compilateurs. Compilez-le sur GCC récent, voyez le résultat obtenu et vérifiez par vous-même si vous ne me croyez pas!
Kos
Essayez d'ajouter std::array<int, fact(2)> et vous verrez que fact () n'est pas évalué au moment de la compilation. C'est juste l'optimiseur GCC qui fait du bon travail.
1
C'est ce que j'ai dit ... suis-je vraiment si peu clair? Voir le dernier paragraphe
Kos
5

Je viens de commencer à basculer un projet vers c ++ 11 et j'ai trouvé une situation parfaitement bonne pour constexpr qui nettoie les méthodes alternatives pour effectuer la même opération. Le point clé ici est que vous ne pouvez placer la fonction dans la déclaration de taille de tableau que lorsqu'elle est déclarée constexpr. Il y a un certain nombre de situations où je peux voir que cela est très utile pour aller de l'avant avec le domaine du code dans lequel je suis impliqué.

constexpr size_t GetMaxIPV4StringLength()
{
    return ( sizeof( "255.255.255.255" ) );
}

void SomeIPFunction()
{
    char szIPAddress[ GetMaxIPV4StringLength() ];
    SomeIPGetFunction( szIPAddress );
}
jgibbs
la source
4
Cela pourrait également s'écrire: const size_t MaxIPV4StringLength = sizeof ("255.255.255.255");
Superfly Jon
static inline constexpr const autoc'est probablement mieux.
JiaHao Xu
3

Toutes les autres réponses sont excellentes, je veux juste donner un exemple sympa d'une chose que vous pouvez faire avec constexpr qui est incroyable. See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h ) est un analyseur HTML et un moteur de modèle au moment de la compilation. Cela signifie que vous pouvez mettre du HTML et en sortir un arbre qui peut être manipulé. Faire l'analyse syntaxique au moment de la compilation peut vous donner un peu de performances supplémentaires.

À partir de l'exemple de page github:

#include <iostream>
#include "seephit.h"
using namespace std;



int main()
{
  constexpr auto parser =
    R"*(
    <span >
    <p  color="red" height='10' >{{name}} is a {{profession}} in {{city}}</p  >
    </span>
    )*"_html;

  spt::tree spt_tree(parser);

  spt::template_dict dct;
  dct["name"] = "Mary";
  dct["profession"] = "doctor";
  dct["city"] = "London";

  spt_tree.root.render(cerr, dct);
  cerr << endl;

  dct["city"] = "New York";
  dct["name"] = "John";
  dct["profession"] = "janitor";

  spt_tree.root.render(cerr, dct);
  cerr << endl;
}
Alcyon
la source
1

Votre exemple de base sert le même argument que celui des constantes elles-mêmes. Pourquoi utiliser

static const int x = 5;
int arr[x];

plus de

int arr[5];

Parce que c'est beaucoup plus maintenable. L'utilisation de constexpr est beaucoup, beaucoup plus rapide à écrire et à lire que les techniques de métaprogrammation existantes.

Chiot
la source
0

Il peut permettre de nouvelles optimisations. constest traditionnellement un indice pour le système de type, et ne peut pas être utilisé pour l'optimisation (par exemple, une constfonction membre peut const_castet modifie l'objet de toute façon, légalement, donc constne peut pas faire confiance pour l'optimisation).

constexprsignifie que l'expression est vraiment constante, à condition que les entrées de la fonction soient const. Considérer:

class MyInterface {
public:
    int GetNumber() const = 0;
};

Si cela est exposé dans un autre module, le compilateur ne peut pas faire confiance qui GetNumber()ne renverra pas de valeurs différentes à chaque appel - même consécutivement sans appels non const entre - parce queconst aurait pu être rejeté dans l'implémentation. (Évidemment, tout programmeur qui a fait cela devrait être abattu, mais le langage le permet, donc le compilateur doit respecter les règles.)

Ajout constexpr:

class MyInterface {
public:
    constexpr int GetNumber() const = 0;
};

Le compilateur peut désormais appliquer une optimisation dans laquelle la valeur de retour de GetNumber()est mise en cache et éliminer les appels supplémentaires à GetNumber(), car constexprc'est une garantie plus forte que la valeur de retour ne changera pas.

AshleysBrain
la source
En fait, const peut être utilisé dans l'optimisation ... C'est un comportement indéfini de modifier une valeur définie const même après un const_castIIRC. Je m'attendrais à ce qu'il soit cohérent pour constles fonctions des membres, mais je devrais vérifier cela avec la norme. Cela signifierait que le compilateur peut y effectuer des optimisations en toute sécurité.
Kos
1
@Warren: peu importe que l'optimisation soit réellement effectuée, c'est juste autorisé. @Kos: c'est une subtilité peu connue que si l' objet d' origine n'a pas été déclaré const ( int xvs. const int x), alors il est sûr de le modifier en const_castsupprimant const sur un pointeur / une référence à lui. Sinon, const_castinvoquerait toujours un comportement indéfini et serait inutile :) Dans ce cas, le compilateur n'a aucune information sur la constance de l'objet d'origine, donc il ne peut pas le dire.
AshleysBrain
@Kos Je ne pense pas que const_cast soit le seul problème ici. La méthode const est autorisée à lire et même à modifier une variable globale. À l'inverse, quelqu'un du thread anpther peut également modifier l'objet const entre les appels.
enobayram
1
Le "= 0" n'est pas valide ici et doit être supprimé. Je le ferais moi-même, mais je ne suis pas sûr que ce soit conforme au protocole SO.
KnowItAllWannabe
Les deux exemples ne sont pas valides: le premier ( int GetNumber() const = 0;) doit déclarer la GetNumber()méthode virtuelle. Le second ( constexpr int GetNumber() const = 0;) n'est pas valide car le spécificateur pur ( = 0) implique que la méthode soit virtuelle, mais les constexpr ne doivent pas être virtuelles (ref: en.cppreference.com/w/cpp/language/constexpr )
stj
-1

Quand utiliser constexpr:

  1. chaque fois qu'il y a une constante de temps de compilation.
BreakBadSP
la source
Bien que je sois d'accord avec vous, cette réponse n'explique pas pourquoi constexpr devrait être préférée aux macros de préprocesseur ou const.
Sneftel
-3

C'est utile pour quelque chose comme

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

int some_arr[MeaningOfLife()];

Liez-le avec une classe de traits ou similaire et cela devient très utile.

plivesey
la source
4
Dans votre exemple, il n'offre aucun avantage sur une constante simple, donc il ne répond pas vraiment à la question.
2011
Ceci est un exemple artificiel, imaginez si la signification de la fonction de vie () provient d'ailleurs, dites une autre fonction ou une # définition ou une série de celle-ci. Vous ne savez peut-être pas ce qu'il renvoie, il peut s'agir d'un code de bibliothèque. Autres exemples, imaginez un conteneur immuable qui a une méthode constexpr size (). Vous pouvez maintenant faire int arr [container.size ()];
plivesey
2
@plivesey pouvez-vous modifier votre réponse avec un meilleur exemple?
Mukesh