2017-10-22 4 views
1

Ich habe dieses Beispiel/Klasse in einem Buch zum Erstellen von SDBM-Hashes zur Kompilierzeit gefunden. Leider kompiliert es nicht (weder mit C++ 11 noch mit C++ 14). Ich bekomme error: call to non-constexpr function. Ich habe es ein bisschen probiert, aber ich kann es nicht funktionieren lassen. Also hier ist meine Frage:Kompilierzeit Hash mit consExpr

  1. Warum funktioniert es nicht und wie könnte es behoben werden? (Es tut mir leid, ich weiß, es ist eine allgemeine Frage, aber zumindest für einen ganz bestimmten Fall)

Full (nicht in Betrieb) Beispiel für Sie zu testen:

#include <iostream> 

template <int stringLength> 
struct SDBMCalculator 
{ 
    static inline int Calculate(const char* const stringToHash, int& value) 
    { 
      int character = SDBMCalculator<stringLength - 1>::Calculate(stringToHash, value); 
      value = character + (value << 6) + (value << 16) - value; 
      std::cout << static_cast<char>(character) << std::endl << value << std::endl << std::endl; 
      return stringToHash[stringLength - 1]; 
    } 

    static inline int CalculateValue(const char* const stringToHash) 
    { 
      int value = 0; 
      int character = SDBMCalculator<stringLength>::Calculate(stringToHash, value); 
      value = character + (value << 6) + (value << 16) - value; 
      std::cout << static_cast<char>(character) << std::endl << value << std::endl << std::endl; 
      return value; 
    } 
}; 

template <> 
struct SDBMCalculator<1> 
{ 
    static inline int Calculate(const char* const stringToHash, int& value) 
    { 
      return stringToHash[0]; 
    } 
}; 


int main() 
{ 
    constexpr int eventID = SDBMCalculator<5>::CalculateValue("Hello"); 
    std::cout << eventID << std::endl; 
} 

viel im Voraus Dank für deine Zeit und Mühe!

+0

Das einzige, was ich sagen kann, kommt die Fehlermeldung wiederholen: Sie rufen eine Funktion, die nicht als constexpr markiert. Ihre 'Calculate' und' CalculateValue' Funktionen sind nicht constexpr. Sie können bei der Auswertung eines constexpr-Wertes keine non-constexpr-Funktionen aufrufen. – CygnusX1

+0

Sie wollen wahrscheinlich 'const' dort. Der Wert der Funktion ist während der Kompilierzeit nicht bekannt, da Sie während der Laufzeit ein Argument angeben. – Ron

Antwort

1

Lesen Sie die Fehlermeldung: Sie rufen eine non-constexpr-Funktion auf, wenn Sie einen conexpr-Wert auswerten. Hast du versucht, das zu beheben?

Wenn Sie alle relevanten Funktionen wie constexpr vornehmen, erhalten Sie einige zusätzliche Fehler, die Ihre Aufmerksamkeit erfordern. Einige Anmerkungen:

  • Stellen Sie sicher, dass Sie mit -std=c++14 kompilieren. C++ 11 ist dafür nicht gut genug.
  • alle Operationen auf std::cout aus SDBMCalculator Funktionen entfernen - diejenigen, die nicht
  • Änderung int in unsigned int in allen relevanten Berechnungen zur Compile-Zeit zulässig sind. Wenn die linke Verschiebung bei int überläuft, erhalten Sie ein undefiniertes Verhalten. Die Verschiebung nach links beim unsignierten Typ wird modulo mit maximalem Wert +1 berechnet.

    error: shift expression ‘(4723229 << 16)’ overflows 
    constexpr int eventID = SDBMCalculator<5>::CalculateValue("Hello") 
    

Mit allen oben genannten Korrekturen wird Ihr Code arbeiten. Ich bekomme das Ergebnis:

2873473298 
+2

@Ron Mit signed int ist das Verhalten nicht definiert. Es gibt keine Funktionalität, von der aus geändert werden kann. –

+1

@ n.m. Aha. Bin dankbar. – Ron

+0

Schnelle und schmerzhafte Antwort. Vielen Dank. Etwas gelernt. – llh

1

So wie die http://en.cppreference.com sagt:

A constexpr variable must satisfy the following requirements:

the full-expression of its initialization, including all implicit conversions, constructors calls, etc, must be a constant expression

Innerhalb der assign Ausdruck:

constexpr int eventID = SDBMCalculator<5>::CalculateValue("Hello"); 

Wir CalculateValue verwenden, die nicht mit constexpr markiert.

Dann haben wir zwei Möglichkeiten:

  • Gerade constexpr

  • zu const ändern oder versuchen CalculateValue eine constexpr Funktion

zu machen wie die erste wirklich langweilig Lassen Sie uns ist Konzentrieren Sie sich auf die zweite eine bessere verstehen, wie konstante Ausdrücke arbeiten!

So beginnen wir mit Markierung CalculateValue als constexpr

static constexpr inline int CalculateValue(const char* const stringToHash) 

Jetzt CalculateValue nur constexpr Funktionen aufrufen müssen. Also müssen wir Calculate eine constexpr auch machen.

static constexpr inline int Calculate(const char* const stringToHash, int& value) 

Und das löst eine Menge Compiler-Fehler aus.

Glücklicherweise können wir feststellen, dass Streams keine gute Sache sind, um in Constexprs gesetzt zu werden, weil Operationen auf Streams nicht mit consExpr markiert sind. (operator<< ist wie eine normale Funktion und es ist nicht Constexpr).

Also entfernen wir die std::cout von dort!

Nun, es ist fast da. Wir müssen auch Calculate in der SDBMCalculator<1> eine constexpr auch machen, weil es von beiden Berechnungsfunktionen aufgerufen werden kann.

Der letzte Code sieht wie folgt aus:

#include <iostream> 

template <int stringLength> 
struct SDBMCalculator 
{ 
    static constexpr inline int Calculate(const char* const stringToHash, int& value) 
    { 
      int character = SDBMCalculator<stringLength - 1>::Calculate(stringToHash, value); 
      value = character + (value << 6) + (value << 16) - value; 
      return stringToHash[stringLength - 1]; 
    } 

    static constexpr inline int CalculateValue(const char* const stringToHash) 
    { 
      int value = 0; 
      int character = SDBMCalculator<stringLength>::Calculate(stringToHash, value); 
      value = character + (value << 6) + (value << 16) - value; 
      return value; 
    } 
}; 

template <> 
struct SDBMCalculator<1> 
{ 
    static constexpr inline int Calculate(const char* const stringToHash, int& value) 
    { 
      return stringToHash[0]; 
    } 
}; 


int main() 
{ 
    constexpr int eventID = SDBMCalculator<5>::CalculateValue("Hello"); 
    std::cout << eventID << std::endl; 
} 

Und natürlich IT KOMPILIEREN NICHT! Wir erhalten:

error: shift expression '(4723229 << 16)' overflows [-fpermissive]

value = character + (value << 6) + (value << 16) - value;

Es ist, weil der Compiler nicht überläuft in konstanten Ausdrücken haben will.

Wir können diesen Fehler unsicher ignorieren und -fpermissive Flag hinzufügen, wenn Sie den Code kompilieren.

g++ example.cpp -o example.exe -fpermissive 

es jetzt compiliert und funktioniert wirklich gut! Der Modifikator für den konstanten Ausdruck bewirkt, dass der Compiler beim Kompilieren den Hash berechnet.

Es ist in Ordnung, weil wir keine Laufzeit-Ressourcen dafür verschwenden, aber wenn Sie überwältigende Menge solcher Vorlagen und Constexprs verwenden und Compiler berechnen alles, was es wird tödlich langsam Kompilation sein!

Ich hoffe, Sie constexpr Verhalten besser zu verstehen, jetzt :)

+0

Danke für Ihre ausführliche Antwort! Sehr schön, ich wünschte, ich könnte zwei Antworten wählen. Ich habe das Gefühl, dass Leute CygnusX1 zuerst lesen und dann zu deinem kommen sollten, wenn sie mehr Hintergrundinformationen brauchen. – llh

+1

Hinweis zum Überlaufteil.Der Fehler ist nicht wegen seiner Kompilierzeit, sondern weil das Überlaufen von Ganzzahlen mit Vorzeichen ein undefiniertes Verhalten ist. constexpr hat es dem Compiler nur erlaubt, das UB zur Kompilierzeit zu erkennen. Was Sie wirklich tun sollten, ist stattdessen 'unsigned int' zu verwenden. Überlauf, der ein wohldefiniertes Verhalten aufweist und sowohl zur Laufzeit als auch zur Kompilierzeit ohne Fehler/Warnungen funktioniert. – CygnusX1

Verwandte Themen