Domanda

Mi sembra che avere un "funzione che restituisce sempre 5" sta rompendo o diluendo il significato di "chiamare una funzione". Ci deve essere una ragione, o di un bisogno di questa capacità o non sarebbe in C ++ 11. Perché è lì?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

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

A me sembra che se ho scritto una funzione che restituisce un valore letterale, e sono venuto fino a un codice-review, qualcuno mi avrebbe detto, dovrei quindi, dichiarare un valore costante invece di scrivere di ritorno 5.

È stato utile?

Soluzione

Supponiamo che fa qualcosa di un po 'più complicato.

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

const int meaningOfLife = MeaningOfLife( 6, 7 );

Ora avete qualcosa che può essere valutato fino a un costante pur mantenendo una buona leggibilità e consentendo l'elaborazione leggermente più complesso di una semplice impostazione di una costante a un numero.

Si prevede in sostanza un aiuto di buono per la manutenibilità in quanto diventa più evidente quello che stai facendo. Prendete max( a, b ) per esempio:

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

La sua una bella scelta semplice lì, ma vuol dire che se si chiama max con valori costanti è esplicitamente calcolato al momento della compilazione e non in fase di esecuzione.

Un altro esempio buono potrebbe essere una funzione DegreesToRadians. reperti tutti gradi più facili da leggere rispetto radianti. Mentre si può sapere che è di 180 gradi in radianti è molto più chiara scritta nel seguente modo:

const float oneeighty = DegreesToRadians( 180.0f );

Un sacco di buone informazioni qui:

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

Altri suggerimenti

Introduzione

constexpr non è stato introdotto come un modo per dire la realizzazione che qualcosa può essere valutato in un contesto che richiede un espressione-costante ; conforme implementazioni ha potuto dimostrare questa prima C ++ 11.

Qualcosa di un'implementazione non può provare è il intento di un certo pezzo di codice:

  • Che cosa è che lo sviluppatore vuole esprimere con questa entità?
  • dovremmo ciecamente permettono codice da utilizzare in un espressione-costante , solo perché capita di lavoro?

Cosa sarebbe il mondo senza constexpr?

Diciamo che si sta sviluppando una biblioteca e rendersi conto che si vuole essere in grado di calcolare la somma di ogni intero nel (0,N] dell'intervallo.

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

La mancanza di intenti

Un compilatore può facilmente dimostrare che la funzione sopra è richiamabile in un espressione-costante se l'argomento passato è noto durante la traduzione; ma non avete dichiarato questo come un intento - è appena successo per essere il caso

.

Ora qualcun altro arriva, si legge la funzione, fa la stessa analisi, come il compilatore; " Oh, questa funzione è utilizzabile in un costante espressione!" , e scrive il seguente pezzo di codice.

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

L'ottimizzazione

, come "awesome" sviluppatore biblioteca, decidere che f dovrebbe memorizzare nella cache il risultato quando viene invocato; chi vorrebbe calcolare lo stesso insieme di valori più e più volte?

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];
}

Il risultato

Con l'introduzione di vostra ottimizzazione stupido, è solo rotto ogni uso della funzione che è accaduto di essere in un contesto in cui un espressione-costante è stato richiesto.

Non hai mai promesso che la funzione era utilizzabile in un espressione-costante , e senza constexpr non ci sarebbe modo di fornire tale promessa.


Allora, perché abbiamo bisogno constexpr?

L'utilizzo primario di constexpr è quello di dichiarare intento .

Se un'entità non è contrassegnato come constexpr - non è mai stato destinato ad essere utilizzato in un espressione-costante ; e anche se lo è, ci affidiamo al compilatore per diagnosticare tale contesto (perché trascura il nostro intento).

Prendere std::numeric_limits<T>::max(): per qualsiasi motivo, questo è un metodo. constexpr sarebbe utile qui.

Un altro esempio: si vuole dichiarare una C-array (o un std::array), che è grande come un altro array. Il modo per farlo in questo momento è in questo modo:

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

Ma non sarebbe meglio essere in grado di scrivere:

int y[size_of(x)];

Grazie a constexpr, è possibile:

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

funzioni constexpr sono veramente bello e una grande aggiunta al C ++. Tuttavia, lei ha ragione in quanto la maggior parte dei problemi risolve possono essere inelegantly lavorato in giro con le macro.

Tuttavia, uno degli usi di constexpr non ha nessun C ++ 03 equivalenti, costanti digitati.

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

Da quello che ho letto, la necessità di constexpr proviene da un problema in metaprogrammazione. classi Trait possono avere costanti rappresentati come funzioni, pensate: numeric_limits :: max (). Con constexpr, questi tipi di funzioni possono essere utilizzate in metaprogrammazione, o come limiti di matrice, ecc ecc.

Un altro esempio al largo della parte superiore della mia testa è che per le interfacce di classe, si consiglia tipi derivati ??definire i propri costanti per alcune operazioni.

Modifica:

Dopo aver frugato su SO, sembra che gli altri sono venuti con qualche https://stackoverflow.com/questions/2111667/compile-time-string-hashing esempi di quello che potrebbe essere possibile con constexprs.

From Stroustrup's speech at "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

Another use (not yet mentioned) is constexpr constructors. This allows creating compile time constants which don't have to be initialized during runtime.

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

Pair that with user defined literals and you have full support for literal user defined classes.

3.14D + 42_i;

There used to be a pattern with 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

I believe constexpr was introduced to let you write such constructs without the need for templates and weird constructs with specialization, SFINAE and stuff - but exactly like you'd write a run-time function, but with the guarantee that the result will be determined in compile-time.

However, note that:

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

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

Compile this with g++ -O3 and you'll see that fact(10) is indeed evaulated at compile-time!

An VLA-aware compiler (so a C compiler in C99 mode or C++ compiler with C99 extensions) may even allow you to do:

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

But that it's non-standard C++ at the moment - constexpr looks like a way to combat this (even without VLA, in the above case). And there's still the problem of the need to have "formal" constant expressions as template arguments.

Have just started switching over a project to c++11 and came across a perfectly good situation for constexpr which cleans up alternative methods of performing the same operation. The key point here is that you can only place the function into the array size declaration when it is declared constexpr. There are a number of situations where I can see this being very useful moving forward with the area of code that I am involved in.

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

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

All of the other answers are great, I just want to give a cool example of one thing you can do with constexpr that is amazing. See-Phit (https://github.com/rep-movsd/see-phit/blob/master/seephit.h) is a compile time HTML parser and template engine. This means you can put HTML in and get out a tree that is able to be manipulated. Having the parsing done at compile time can give you a bit of extra performance.

From the github page example:

#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;
}

Your basic example serves he same argument as that of constants themselves. Why use

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

over

int arr[5];

Because it's way more maintainable. Using constexpr is much, much faster to write and read than existing metaprogramming techniques.

It can enable some new optimisations. const traditionally is a hint for the type system, and cannot be used for optimisation (e.g. a const member function can const_cast and modify the object anyway, legally, so const cannot be trusted for optimisation).

constexpr means the expression really is constant, provided the inputs to the function are const. Consider:

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

If this is exposed in some other module, the compiler can't trust that GetNumber() won't return different values each time it's called - even consecutively with no non-const calls in between - because const could have been cast away in the implementation. (Obviously any programmer who did this ought to be shot, but the language permits it, therefore the compiler must abide by the rules.)

Adding constexpr:

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

The compiler can now apply an optimisation where the return value of GetNumber() is cached and eliminate additional calls to GetNumber(), because constexpr is a stronger guarantee that the return value won't change.

When to use constexpr:

  1. whenever there is a compile time constant.

It's useful for something like

// constants:
const int MeaningOfLife = 42;

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

int some_arr[MeaningOfLife()];

Tie this in with a traits class or the like and it becomes quite useful.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top