2014-10-27 9 views
12

Ich bin mit etwas Spielzeug Code mit C++ 11 herumspielen, um ein bisschen mehr darüber herauszufinden, wie die Dinge funktionieren. Dabei stieß ich auf das folgende Problem, das zu vereinfacht unten:conexpr Funktion Parameter als Vorlage Argumente

template <int x, int y> 
class add { 
public: 
    static constexpr int ret = x + y; 
}; 

constexpr int addFunc(const int x, const int y) { 
    return add<x,y>::ret; 
} 

int main() { 
    const int x = 1; 
    const int y = 2; 
    cout << add<x,y>::ret << endl; // Works 
    cout << addFunc(1,2) << endl; // Compiler error 
    return 0; 
} 

Ich bin mit GCC 4.8.1 und der Ausgang ist:
‚x‘ ist keine Konstante Ausdruck in Template-Argument für den Typ int‘ "
‚y‘ist kein konstanter Ausdruck in Template-Argument für Typ‚int‘

Was genau ist der Unterschied zwischen den beiden Arten ich versuche add::ret zu berechnen? Diese beiden Werte sollten zum Zeitpunkt der Kompilierung verfügbar sein.

+3

'conexpr'-Funktionen müssen zur Laufzeit ausgeführt werden können. – chris

+1

Nun ... ja. Warum kann das nicht speziell zur Kompilierzeit ausgewertet werden? – Danny

+6

Um zu erarbeiten: 'conexpr'-Funktionen müssen in der Lage sein, zur Laufzeit ausgeführt werden, und Ihre' constexpr'-Funktion würde fehlschlagen, wenn sie mit einem Wert aufgerufen wird, der keine Kompilierzeitkonstante ist, so dass Ihre 'constexpr'-Funktion nicht gültig ist . Was Sie suchen, ist nicht, was "constexpr" bietet, und ist auch nicht etwas, das C++ in einer anderen Form anbietet. Was am nächsten kommt, ist 'addFunc' eine Template-Funktion mit' int x' und 'int y' Template-Parametern. – hvd

Antwort

7

Sie sagen dem Compiler, dass addFunc wäre ein conetexpr. Aber es hängt von Parametern ab, die nicht consExpr selbst sind, also erstickt der Compiler schon daran. Wenn Sie sie als const markieren, bedeutet dies, dass Sie sie nicht im Funktionskörper ändern werden, und die spezifischen Aufrufe, die Sie an die Funktion richten, werden an dieser Stelle nicht berücksichtigt.

Es gibt einen Weg Sie den Compiler machen verstehen Sie nur Zeitkonstanten addFunc passieren werden kompilieren: Stellen Sie die Parameter ein Template-Parameter selbst:

template <int x, int y> 
constexpr int addFunc() { 
    return add<x,y>::ret; 
} 

Dann rufen als

cout << addFunc<1,2>() << endl; 
5

Der Compiler weiß nicht, ob x und y bei der Kompilierung immer zur Verfügung steht als konstante Werte (Ausdruck), und was mehr ist, C++ 14.11 nicht constexpr Funktionsparameter, so dass es nicht unterstützt keine x Weg und y kann als Parameter für das Template add <> in addFunc verwendet werden.

7

Wenn Ihr Zweck ist nur Code ein wenig zu verkürzen, in C++ 14 Sie können variable Vorlage erstellen:

template <int x, int y> 
constexpr int addVar = x + y; 

cout << addVar<5, 6> << endl; // Works with clang 3.5, fails on GCC 4.9.1 

GCC 5 will also support this.

3

Funktionsparameter einer constexpr-Funktion sind keine konstanten Ausdrücke. Die Funktion ist constexpr nach außen (wie sie aufrufen könnte zu einem konstanten Ausdruck führen), aber Berechnungen im Inneren sind genauso wie constexpr, wie sie in einer normalen Funktion wäre.

Template-Argumente erfordern konstante Ausdrücke. Dies sind die entscheidenden Voraussetzungen für konstante Ausdrücke, die in Ihrem Code nicht erfüllt werden und damit die Compiler-Fehler erzeugen ([expr.const]/2, Hervorhebung von mir):

A bedingungsausdruck ist ein Kern konstanten Ausdruck es sei denn, es eine der folgenden als potenziell bewertet subexpression (3.2) [...] beinhaltet:

- ein L-Wert-to-rvalue Umwandlung (4.1), es sei denn, es zu

  • einer glvalue von integralem oder Aufzählungstyp angewandt wird, die mit einer vorhergehenden Initialisierung ein nicht-flüchtigen const Objekt bezieht, initialisiert mit einem konstanten Ausdruck, oder
  • a glvalue von literal Art, die ein nicht-flüchtiges Objekt bezieht sich auf definiert mit constexpr oder dass bezieht sich auf ein Unterobjekt eines solchen Objekt oder
  • a glvalue von literalen Typ, der auf einem nichtflüchtigen temporären Objekt bezieht deren Lebensdauer nicht beendet ist, initialisiert mit einem konstanten Ausdruck;

Sie bewerben sich ein L-Wert-to-rvalue Umwandlung zu den Parametern, sie als Vorlage Argumente zu übergeben.

Das erste Aufzählungselement ist nicht anwendbar, da der Funktionsparameter weder vorausgehend initialisiert noch bekannt ist, dass er mit einem konstanten Ausdruck initialisiert wird, und auch das zweite und dritte nicht (insbesondere Funktionsparameter sollen nicht deklariert werden constexpr).