Question

Il me semble que d'avoir une « fonction qui renvoie toujours 5 » se brise ou diluer le sens de « l'appel d'une fonction ». Il doit y avoir une raison ou un besoin de cette capacité ou il 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 je l'ai écrit une fonction qui renvoie une valeur littérale, et je suis venu à un code d'examen, quelqu'un me dire, je puis déclarer une valeur constante au lieu de retour écriture 5.

Était-ce utile?

La solution

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

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

const int meaningOfLife = MeaningOfLife( 6, 7 );

Maintenant, vous avez quelque chose qui peut être évalué jusqu'à une constante tout en conservant une bonne lisibilité et en permettant un traitement un peu plus complexe qu'une simple fixation constante à un certain nombre.

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

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

Son choix assez simple là, mais cela signifie que si vous appelez max avec des valeurs constantes, il est explicitement calculée au moment de la compilation et non à l'exécution.

Un autre bon exemple serait une fonction DegreesToRadians. Tout le monde trouve des degrés plus facile à lire que radians. Même si vous savez que 180 degrés est en radians il est beaucoup plus claire par écrit comme suit:

const float oneeighty = DegreesToRadians( 180.0f );

Beaucoup de bonnes infos ici:

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

Autres conseils

Introduction

constexpr n'a pas été présenté comme un moyen de dire la mise en œuvre que quelque chose peut être évaluée dans un contexte qui exige une expression constante ; implémentations conformes a été en mesure de prouver avant C ++ 11.

Quelque chose d'une mise en œuvre ne peut pas prouver le intention d'un certain morceau de code:

  • Qu'est-ce que le développeur veut exprimer avec cette entité?
  • Devons-nous permettre aveuglément le code à utiliser dans expression constante , juste parce qu'il arrive au travail?

Que serait le monde sans constexpr?

Disons que vous développez une bibliothèque et se rendre compte que vous voulez être en mesure de calculer la somme de tout entier dans l'intervalle (0,N].

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

L'absence d'intention

Un compilateur peut facilement prouver que la fonction ci-dessus est appelable dans un expression constante si l'argument passé est connu lors de la traduction; mais vous ne l'avez pas déclaré cela comme une intention - il est arrivé juste être 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 la pièce de code suivante.

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

L'optimisation

Vous, en tant que "awesome" développeur bibliothèque, décider que f doit mettre en cache le résultat lorsqu'il est invoqué; qui voudrait calculer le même ensemble de valeurs reprises?

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 stupide, vous vient de se casser chaque utilisation de votre fonction qui se trouvait dans un contexte où expression constante était nécessaire.

Vous n'a jamais promis que la fonction était utilisable dans un expression constante , et sans constexpr il 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 intention .

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

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

Un autre exemple: vous voulez déclarer un C-tableau (ou un std::array) qui est aussi grand qu'un autre tableau. La façon de le faire en ce moment est comme ceci:

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

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

int y[size_of(x)];

Merci à constexpr, vous pouvez:

template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
    return N;
}

Fonctions constexpr sont vraiment sympa et un excellent ajout à c ++. Cependant, vous avez raison dans ce que la plupart des problèmes peuvent être il résout inélégante travaillé autour des macros.

Cependant, l'une des utilisations de constexpr n'a pas C ++ 03 équivalent, constantes typées.

// 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;

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

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

Edit:

Après farfouillé sur le SO, il semble que d'autres sont venus avec certains https://stackoverflow.com/questions/2111667/compile-time-string-hashing exemples de ce qui pourrait être possible avec constexprs.

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

Une autre utilisation (pas encore mentionné) est constexpr constructeurs. Cela permet de créer des constantes de temps de compilation qui ne doivent pas être initialisé lors de l'exécution.

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

Paire que littéraux défini par l'utilisateur et vous avez prise en charge complète pour les classes définies par l'utilisateur littéral.

3.14D + 42_i;

Il y avait autrefois un modèle avec metaprogramming:

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 constexpr a été introduit pour vous permettre d'écrire de telles constructions sans avoir besoin de modèles et des 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 déterminer dans la compilation.

Cependant, notez que:

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

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

Compile cela avec g++ -O3 et vous verrez que fact(10) est en effet evaulated à la compilation!

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

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

Mais qu'il est non standard C ++ pour le moment - ressemble constexpr comme un moyen de lutter contre ce (même sans VLA, dans le cas ci-dessus). Et il y a encore le problème de la nécessité d'avoir « formel » des expressions constantes comme arguments de modèle.

viennent de commencer la commutation d'un projet à c ++ 11 et est tombé sur une situation parfaitement bon pour constexpr qui nettoie les méthodes alternatives d'effectuer la même opération. Le point clé ici est que vous ne pouvez placer la fonction dans la taille tableau déclaration lorsqu'elle est déclarée constexpr. Il y a un certain nombre de situations où je peux voir ce qui est très utile aller de l'avant avec la zone de code que je suis impliqué dans.

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

void SomeIPFunction()
{
    char szIPAddress[ GetMaxIPV4StringLength() ];
    SomeIPGetFunction( szIPAddress );
}

Toutes les autres réponses sont grands, je veux juste donner un exemple de frais d'une chose que vous pouvez faire avec constexpr qui est incroyable. See-Phit ( https://github.com/rep- movsd / voir-Phit / blob / maître / seephit.h ) est un temps de compilation analyseur HTML et moteur de modèle. Cela signifie que vous pouvez mettre HTML et sortir un arbre qui peut être manipulé. Avoir l'analyse effectuée au moment de la compilation peut vous donner un peu de performances supplémentaires.

Dans l'exemple de la 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;
}

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

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

sur

int arr[5];

Parce qu'il est beaucoup plus maintenable. L'utilisation constexpr est beaucoup, beaucoup plus rapide à écrire et lire que les techniques existantes Méta.

Il peut permettre de nouvelles optimisations. const est traditionnellement une pointe pour le système de type, et ne peut pas être utilisé pour l'optimisation (par exemple une fonction membre const peut const_cast et modifier l'objet de toute façon, légalement, de sorte const ne peut faire confiance pour l'optimisation).

constexpr signifie l'expression vraiment est constant, à condition que les entrées de la fonction sont const. Considérez:

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

Si cela est exposé dans un autre module, le compilateur ne peut pas faire confiance que GetNumber() ne reviendra pas des valeurs différentes à chaque fois qu'il est appelé - même consécutivement sans appels non const entre - parce const aurait pu être jetés dans la mise en oeuvre. (Il est évident que tout programmeur qui a fait cela devrait être tiré, mais les permis linguistiques, il est donc le compilateur doit respecter les règles.)

Ajout constexpr:

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

Le compilateur peut maintenant appliquer une optimisation où la valeur de retour de GetNumber() est mise en mémoire cache et d'éliminer les appels supplémentaires à GetNumber(), car constexpr est une garantie plus forte que la valeur de retour ne changera pas.

Quand utiliser constexpr:

  1. chaque fois qu'il ya une constante de compilation.

Il est utile pour quelque chose comme

// constants:
const int MeaningOfLife = 42;

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

int some_arr[MeaningOfLife()];

Tie cela avec une classe ou des traits similaires, et il devient tout à fait utile.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top