Frage

Es scheint mir, dass eine „Funktion, die immer 5 zurückgibt“, die bricht oder Verdünnung der Bedeutung von „Aufruf einer Funktion“. Es muss für diese Fähigkeit ein Grund oder eine Notwendigkeit sein, oder es wäre nicht in C ++ 11. Warum ist es da?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

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

Es scheint mir, dass, wenn ich eine Funktion geschrieben, die einen wörtlichen Wert zurück, und ich kam zu einem Code-Review-up, jemand würde mir sagen, ich soll dann erklären, einen konstanten Wert anstelle dem Schreiben Rückkehr 5.

War es hilfreich?

Lösung

Angenommen, es macht etwas ein wenig komplizierter.

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

const int meaningOfLife = MeaningOfLife( 6, 7 );

Jetzt haben Sie etwas, das auf einen konstanten ausgewertet gelegt werden kann, während ein gute Lesbarkeit zu erhalten und damit etwas komplexere Verarbeitung als nur eine Konstante zu einer Reihe zu setzen.

Es gibt im Grunde eine gute Hilfe für die Wartbarkeit, wie es deutlicher wird das, was Sie tun. Nehmen Sie max( a, b ) zum Beispiel:

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

Es ist eine ziemlich einfache Wahl gibt, aber es bedeutet, dass, wenn Sie rufen max mit konstanten Werten wird explizit bei der Kompilierung und nicht zur Laufzeit.

Ein weiteres gutes Beispiel wäre eine DegreesToRadians Funktion sein. Jeder findet Grad leichter zu lesen als Radiant. Während Sie, dass 180 Grad wissen kann, ist in Radian ist es viel klarer geschrieben wie folgt:

const float oneeighty = DegreesToRadians( 180.0f );

Es gibt viele gute Infos hier:

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

Andere Tipps

Einführung

constexpr nicht als Möglichkeit eingeführt wurde, die Umsetzung zu sagen, dass etwas in einem Kontext beurteilt werden kann, die erfordern einen konstanten Ausdruck ; Implementierungen ist es gelungen, zu beweisen, dies vor dem C ++ 11.

konform

Etwas eine Implementierung nicht beweisen kann, ist die Absicht von einem bestimmten Stück Code:

  • Was ist es, dass die Entwickler mit dieser Einheit zum Ausdruck bringen wollen?
  • sollten wir blind erlauben Code in einem verwendet werden konstanter Ausdruck , nur weil es an die Arbeit geschieht?

Was wäre die Welt ohne constexpr?

Angenommen, Sie haben eine Bibliothek und realize entwickeln, dass Sie die Summe aller integer zu berechnen, die in dem Intervall (0,N] der Lage sein wollen.

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

Der Mangel an Absicht

Ein Compiler kann leicht beweisen, dass die obige Funktion aufrufbar ist in einem konstantem Ausdruck , wenn das Argument übergeben wird während der Übersetzung bekannt; aber Sie haben nicht erklärt dies als Absicht - es ist nur zufällig der Fall sein

.

Jetzt jemand anderes kommt, liest Ihre Funktion hat die gleiche Analyse wie der Compiler; " Oh, diese Funktion ist verwendbar in einem konstanten Ausdruck!" und schreibt das folgende Stück Code.

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

Die Optimierung

Sie als "awesome" Bibliothek Entwickler, entscheiden, dass f sollte das Ergebnis zwischenspeichern, wenn sie aufgerufen werden; wer möchte den gleichen Satz von Werten über berechnen und über?

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

Das Ergebnis

Durch Ihre dumme Optimierung Einführung brach man nur jede Nutzung Ihrer Funktion, die in einem Kontext geschehen, wo ein konstanter Ausdruck erforderlich war.

Du hast versprochen, nie, dass die Funktion war, die bei einem konstanter Ausdruck , und ohne constexpr gäbe es keine Möglichkeit für die Bereitstellung dieser Versprechen.


So

, warum brauchen wir constexpr?

Die primäre Verwendung von constexpr ist zu declare Absicht .

Wenn ein Unternehmen nicht als constexpr markiert ist - es wurde nie in einem Konstante Ausdruck verwendet werden soll ; und selbst wenn es ist, verlassen wir uns auf den Compiler so Kontext zu diagnostizieren (weil es unsere Absicht absieht).

Nehmen Sie std::numeric_limits<T>::max(): aus welchem ??Grund, ist dies ein Verfahren ist. constexpr von Vorteil wäre hier.

Ein weiteres Beispiel: Sie wollen einen C-Array deklarieren (oder einen std::array), die so groß wie ein anderes Array ist. Die Art und Weise dies im Moment zu tun, ist in etwa so:

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

Aber wäre es nicht besser in der Lage zu schreiben sein:

int y[size_of(x)];

Dank constexpr können Sie:

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

constexpr Funktionen sind wirklich schön und eine große Bereicherung für c ++. Sie sind jedoch richtig, dass die meisten Probleme kann es löst unelegant mit Makros, um gearbeitet werden.

Allerdings ist eine der Anwendungen von constexpr hat keine C ++ 03 äquivalent, typisierte Konstanten.

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

Von dem, was ich gelesen habe, kommt das Bedürfnis nach constexpr von einem Problem in metaprogramming. Trait Klassen können Konstanten als Funktionen dargestellt, denken: numeric_limits :: max (). Mit constexpr, können diese Arten von Funktionen in metaprogramming verwendet werden, oder als Array-Grenzen, etc etc.

Ein weiteres Beispiel aus der Spitze von meinem Kopf würde für Klasse-Schnittstellen sein, dass Sie mögen abgeleitete Typen ihre eigenen Konstanten für einige Operation definieren.

Edit:

Nach dem Einschalten SO stochern, es sieht aus wie andere haben sich mit einige Beispiele von dem, was mit constexprs möglich sein könnte.

Von Stroustrup Rede auf "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

Eine weitere Verwendung (noch nicht erwähnt) ist constexpr Bauer. Dies ermöglicht es der Kompilierung Konstanten zu schaffen, die nicht während der Laufzeit initialisiert werden müssen.

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

Paar, dass mit benutzerdefinierten Literale und Sie haben die volle Unterstützung für wörtliche Benutzer definierten Klassen.

3.14D + 42_i;

Es verwendet ein Muster mit metaprogramming zu sein:

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

Ich glaube, constexpr eingeführt wurde, lassen Sie solche Konstrukte ohne die Notwendigkeit von Vorlagen und seltsame Konstrukte mit Spezialisierung SFINAE und solche Sachen schreiben - aber genau wie Sie eine Laufzeitfunktion schreiben würde, aber mit der Garantie, dass das Ergebnis in Compile-Zeit bestimmt werden.

Beachten Sie jedoch, dass:

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

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

Compile dies mit g++ -O3 und Sie werden sehen, dass fact(10) in der Tat zum Zeitpunkt der Kompilierung evaulated ist!

Ein VLA-aware-Compiler (so ein C-Compiler in C99-Modus oder C ++ Compiler mit C99-Erweiterungen) können Sie sogar erlauben, zu tun:

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

Aber, dass es Nicht-Standard-C ++ zum Zeit - constexpr sieht aus wie eine Art und Weise, dies zu bekämpfen (auch ohne VLA, im obigen Fall). Und es gibt immer noch das Problem der Notwendigkeit, „formal“ konstanten Ausdrücke als Vorlage Argument zu haben.

Haben Sie gerade begonnen, ein Projekt zu c Umschalten ++ 11 und stieß auf eine ganz gute Situation für constexpr, die die gleiche Operation durchführen alternative Methoden bereinigt. Der entscheidende Punkt ist, dass Sie nur die Funktion in die Array-Größe Deklaration platzieren können, wenn es constexpr deklariert wird. Es gibt eine Reihe von Situationen, in denen ich das sehr nützlich sein sehen vorwärts mit dem Codebereich bewegt, dass ich beteiligt bin.

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

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

Alle anderen Antworten sind groß, ich will nur ein kühles Beispiel für eine Sache, um Ihnen mit constexpr tun können, die erstaunlich ist. See-Phit ( https://github.com/rep- movsd / see-phit / Blob / Master / seephit.h ) ist eine Kompilierung HTML-Parser und Template-Engine. Diese Mittel können Sie HTML-Hineinlegen und einen Baum herauskommen, die manipuliert werden können. die Analyse bei der Kompilierung getan zu haben, können Sie ein bisschen mehr Leistung.

Von dem Github Seite Beispiel:

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

Ihr grundsätzliches Beispiel dient er gleiche Argument wie die Konstanten selbst. Warum Gebrauch

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

über

int arr[5];

Weil es viel mehr wartbar. Mit constexpr ist viel, viel schneller zu Schreib- und Lese als bestehende Metaprogrammierung Techniken.

Es kann einige neue Optimierungen ermöglichen. const traditionell ist ein Hinweis für das Typsystem und kann nicht für die Optimierung verwendet werden (beispielsweise eine const Elementfunktion kann das Objekt trotzdem const_cast und modifizieren, rechtlich, so const nicht für die Optimierung vertraut werden kann).

constexpr bedeutet der Ausdruck wirklich konstant ist, vorausgesetzt, dass die Eingaben an die Funktion sind konst. Bedenken Sie:

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

Wenn dies in einem anderen Modul ausgesetzt ist, kann der Compiler nicht, dass GetNumber() Vertrauen wird nicht zurückkehrt verschiedene Werte jedes Mal, es heißt - auch nacheinander ohne nicht-const Anrufe zwischendurch - weil const Besetzung gewesen sein könnte entfernt die Umsetzung. (Offensichtlich jeder Programmierer, der tat dies sollte Schuss sein, aber die Sprache erlaubt es, damit der Compiler die Regeln halten müssen.)

Hinzufügen constexpr:

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

Der Compiler kann nun eine Optimierung gelten, wenn der Rückgabewert von GetNumber() zwischengespeichert wird und beseitigen zusätzliche Anrufe zu GetNumber(), weil constexpr eine stärkere Garantie, dass der Rückgabewert wird sich nicht ändern.

Wenn constexpr verwenden:

  1. wann immer es eine Kompilierung konstant ist.

Es ist nützlich, für so etwas wie

// constants:
const int MeaningOfLife = 42;

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

int some_arr[MeaningOfLife()];

Tie dies in einer Klasse Züge oder dergleichen, und es wird sehr nützlich.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top