2016-02-08 16 views
5

Ich habe vor kurzem eine Frage hier gestellt (Detecting instance method constexpr with SFINAE), wo ich versuchte, einige Competexpr-Erkennung zur Kompilierzeit zu tun. Irgendwann habe ich herausgefunden, dass man noexcept dazu ausnutzen kann: jeder konstante Ausdruck ist auch noexcept. Also habe ich die folgenden Maschinen zusammen:Constexpr declltype

template <class T> 
constexpr int maybe_noexcept(T && t) { return 0; } 
... 
constexpr bool b = noexcept(maybe_noexcept(int{})); 

Dies funktioniert und b ist wahr, wie man erwarten würde, als Null-Initialisierung ein int ist ein konstanter Ausdruck. Es gibt auch korrekt Null wenn es sollte (wenn ich int zu einem anderen geeigneten Typ ändern).

Als nächstes wollte ich überprüfen, ob etwas constexpr Move Constructible ist. Also tat ich dies:

constexpr bool b = noexcept(maybe_noexcept(int(int{}))); 

Und wieder funktioniert dies richtig für int oder ein benutzerdefinierter Typ. Dies überprüft jedoch, ob der Typ sowohl einen consExpr-Standardkonstruktor als auch einen constexpr-Move-Konstruktor hat. Also, um dies zu umgehen, habe ich versucht zu declval zu ändern:

constexpr bool b = noexcept(maybe_noexcept(int(declval<int>()))); 

Dies führt zu b falsch in gcc 5.3.0 zu sein (kann nicht klappern für irgendetwas davon verwenden, da klappert nicht richtig konstant machen Ausdrücke noexcept). Kein Problem, sage ich, muss sein, denn declval ist (interessanterweise) nicht markiert constexpr. Also schreibe ich meine eigene naive Version:

template <class T> 
constexpr T&& constexpr_declval() noexcept; 

Ja, das ist naiv im Vergleich zu, wie die Standard-Bibliothek tut es, wie es auf Leere und wahrscheinlich andere Dinge zu ersticken, aber es ist jetzt in Ordnung. Also versuche ich wieder:

constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>()))); 

Diese immer noch nicht funktioniert, ist b immer falsch. Warum wird dies nicht als konstanter Ausdruck angesehen? Ist das ein Compiler Bug, oder verstehe ich nicht grundlegend über constexpr? Es scheint, als ob es eine seltsame Interaktion zwischen constexpr und nicht evaluierten Kontexten gibt.

+1

@Cameron Der zweite Teil von dem, was Sie gesagt haben, ist sicherlich wahr, aber das erste technisch ist nicht. Jeder konstante Ausdruck * ist * "noexcept", das ist es nicht. Die Rückgabe einer "constexpr" -Funktion ist jedoch nicht immer ein konstanter Ausdruck. –

+1

Ihr Funktionsaufruf ist kein konstanter Ausdruck, weil er undefiniert ist ([expr.const] /2.3) – 0x499602D2

Antwort

6

constexpr Ausdrücke müssen definiert werden. Deines ist nicht definiert, also ist int(constexpr_declval<int>()) in diesem Fall nicht constexpr.

Das bedeutet maybe_noexcept(int(constexpr_declval<int>())) ist kein constexpr, also nicht noexcept.

Und der Compiler gibt false ordnungsgemäß zurück.

Sie können UB auch nicht in einer constexpr aufrufen.

Ich kann mir keinen Weg vorstellen, einen constexpr Verweis auf beliebige Daten zu machen. Ich dachte, ein constexpr Puffer von ausgerichteten Speicher neu interpretiert als eine Referenz auf den Datentyp, aber das ist UB in vielen Kontexten, daher nicht constexpr.

Im Allgemeinen ist dies nicht möglich. Stellen Sie sich vor, Sie hatten einen Klasse, deren Zustand bestimmt, ob der Aufruf der Methode ist constexpr:

struct bob { 
    int alice; 
    constexpr bob(int a=0):alice(a) {} 
    constexpr int get() const { 
    if (alice > 0) throw std::string("nope"); 
    return alice; 
    } 
}; 

jetzt ist bob::getconstexpr oder nicht? Es ist, wenn Sie eine constexpr bob mit einem nicht positiven alice konstruiert haben, und ...Es ist nicht, wenn nicht.

Sie können nicht sagen "Geben Sie diesen Wert constexpr und sagen Sie mir, ob ein Ausdruck constexpr ist". Selbst wenn Sie könnten, würde es das Problem im Allgemeinen nicht lösen, weil sich der Zustand eines constexpr Parameters ändern kann, wenn ein Ausdruck constexpr ist oder nicht!

Noch mehr Spaß, bob().get()ist conexpr, während bob(1).get() nicht ist. So hat Ihr erster Versuch (Standard den Typ konstruieren) sogar die falsche Antwort gegeben: Sie können testen, dann die Aktion ausführen und die Aktion wird fehlschlagen.

Das Objekt ist effektiv ein Parameter für die Methode, und ohne den Status von al-Parametern können Sie nicht feststellen, ob eine Funktion constexpr ist.

Der Weg, um festzustellen, ob ein Ausdruck constexpr ist, ist es in einem constexpr Kontext zu starten und sehen, ob es funktioniert.

+0

Es scheint also tatsächlich unmöglich zu sein, genau zu testen, ob die consxpr-Instanzmethoden für eine Klasse existieren, weil Sie eine benötigen Instanz, um dies zu tun, und es gibt keine Möglichkeit, eine consxpr-Instanz zu erhalten, die sowohl tatsächlich definiert ist und nicht die Existenz eines Konstruktors annimmt. Ist das eine angemessene Beschreibung der Situation? –

+1

@NirFriedman Nun, Sie können Argumente übergeben, um das Objekt zu konstruieren. ;) Im Allgemeinen ist "conetexpr" kontextabhängig - Sie müssen fast alles über den Aufruf und seine Parameter wissen. Einer der Parameter ist das Objekt *, das den Aufruf ausführt *: wenn es nicht "consxpr" ist, ist der Ausdruck (im Allgemeinen) nicht. Zu sagen "wenn ich vorginge, dass dieses Argument" conexpr "ist, auch wenn es nicht passiert, wird nicht unterstützt, soweit ich weiß. – Yakk

+0

Haha in der Tat. Alles klar, deine Antwort + Kommentar war sehr informativ, danke. –