In C ++ 11 constexpr-Funktionen ist eine zweite Anweisung wie eine assert()
nicht möglich. A static_assert()
ist in Ordnung, würde aber nicht funktionieren, wenn die Funktion als "normale" Funktion aufgerufen wird. Der Komma-Operator könnte kommen, um wrto zu helfen. Das assert()
ist aber hässlich und einige Tools spucken Warnungen darüber aus.
Betrachten Sie einen solchen "Getter", der neben der Behauptung durchaus verständlich ist. Aber ich möchte eine Art Zusicherung für die Laufzeit und die Kompilierungszeit behalten, kann aber nicht einfach überladen, abhängig vom Kontext 'constexpr'.
template<int Size>
struct Array {
int m_vals[Size];
constexpr const int& getElement( int idx ) const
{
ASSERT( idx < Size ); // a no-go for constexpr funcs in c++11
// not possible, even in constexpr calls as being pointed out, but what I would like:
static_assert( idx < Size, "out-of-bounds" );
return m_vals[idx];
}
};
Nebenbedingungen: C ++ 11, kein Heap, keine Ausnahmen, keine Compilerspezifikationen.
Beachten Sie, wie Kommentatoren betonten (danke!), static_assert
Das Argument ist nicht möglich (wäre aber nett). Der Compiler hat mir in dieser Situation einen anderen Fehler beim Zugriff außerhalb der Grenzen gemeldet.
static_assert
abhängig überhaupt nicht verwendenidx
. Sie können nur dann einen falschen Wert diagnostizieren,idx
wenn die Funktion in einem Kontext verwendet wird, der einen konstanten Ausdruck erfordert, indem Sie die Auswertung eines Konstrukts erzwingen, das es nicht zu einem konstanten Ausdruck macht. Außerhalb eines solchen Kontexts können Sie den Wert zur Kompilierungszeit niemals überprüfen.Antworten:
Etwas wie
Es wird ein Kompilierungszeitfehler bei einem Assertionsfehler ausgegeben, wenn es in einem Kontext verwendet wird , der einen konstanten Ausdruck erfordert (weil es eine Nichtfunktion aufruft
constexpr
).Andernfalls schlägt es zur Laufzeit mit einem Aufruf von
assert
(oder Ihrem Analog) fehl .Soweit ich weiß, ist dies das Beste, was Sie tun können. Es gibt keine Möglichkeit, den Wert von
idx
zu verwenden, um eine Überprüfung zur Kompilierungszeit außerhalb des Kontexts zu erzwingen , der konstante Ausdrücke erfordert .Die Syntax des Kommaoperators ist nicht gut, aber die C ++ 11-
constexpr
Funktionen sind sehr eingeschränkt.Wie Sie bereits bemerkt haben, wird undefiniertes Verhalten natürlich trotzdem diagnostiziert, wenn die Funktion in einem Kontext verwendet wird, der einen konstanten Ausdruck erfordert.
Wenn Sie wissen, dass
assert
(oder Ihr Analogon) sich nicht auf etwas erweitert, das in einem konstanten Ausdruck verboten ist, wenn die Bedingung ausgewertet wird,true
sondern wenn dies ausgewertet wirdfalse
, können Sie es direkt verwenden, anstatt die vonmy_assert
mir erstellte Indirektion zu überspringen in meinem Code.quelle
(void)0
in demNDEBUG
Fall undvoid()
in der anderen? Oder ist es wirklich dasselbe?(void)0
ist ein No-Op, es wird zu nichts kompiliert (was Sie wollen, wennNDEBUG
es definiert ist), während Sie das benötigen,void()
damit der zweite und dritte Operand des bedingten Operators den gleichen Typ habenvoid
.(void)0
wäre in jedem Fall auch in Ordnung. Ich habe es gerade im ersten Fall ersetzt, davoid()
es je nach Kontext auch als Funktionstyp ohne Parameter und ohne Rückgabetyp analysiert werden kann. In den Unterausdrücken im zweiten Fall kann dies nicht analysiert werden.Besser als ein Komma-Ausdruck können Sie eine ternäre Bedingung verwenden. Der erste Operand ist Ihr Assertionsprädikat, der zweite Operand ist Ihr Erfolgsausdruck. Da der dritte Operand ein beliebiger Ausdruck sein kann - auch einer, der in einem konstanten C ++ 11-Kontext nicht verwendet werden kann -, können Sie mit einem Lambda die
ASSERT
Einrichtung Ihrer Bibliothek aufrufen :Erklärung des Lambda-Körpers:
ASSERT(false && (pred))
soll sicherstellen, dass Ihre Assertionsmaschinerie mit einem geeigneten Ausdruck (zur Stringifizierung) aufgerufen wird.struct nxg { nxg() {} } nxg
dient der Zukunftssicherheit, um sicherzustellen, dass beim Kompilieren in C ++ 17 oder höherNDEBUG
das Lambda immer noch nicht vorhanden istconstexpr
und die Behauptung daher im Kontext der Konstantenbewertung erzwungen wird.return (success)
gibt es aus zwei Gründen: um sicherzustellen, dass der zweite und der dritte Operand den gleichen Typ haben, und damitNDEBUG
dersuccess
Ausdruck unabhängig davon zurückgegeben wird , ob Ihre Bibliothek dies respektiertpred
. (pred
wird ausgewertet , aber Sie hoffen, dass Assertionsprädikate billig zu bewerten sind und keine Nebenwirkungen haben.)Anwendungsbeispiel:
quelle
[&]
/[&] -> decltype((success))
um Referenzen zu erhalten.pred
Bewertung billig sein sollte, aber sie sind nicht immer. Als allgemeinesASSERT_EXPR
Makro würde ich es also nicht empfehlen. Ich mache manchmal teure Anrufe in einer Behauptung selbst (z. B. um Invarianten zu überprüfen).NDEBUG
zum Aktivieren von Laufzeit- Asserts aktiviert sind, die Assets zur Kompilierungszeit überprüft werden sollen.constexpr
Dies kann sichergestellt werden, indem der Body des Fehlerfalls Lambda nicht verwendet wird. Dies hat jedoch die Kosten für die Auswertung und das Verwerfen des Prädikats zur Laufzeit inNDEBUG
. Andernfalls könnten Sie das Makro unterNDEBUG
einfach definierenreturn (success);
.static_assert
kann hier nicht verwendet werden. Das Argument für eineconstexpr
Funktion ist in einem Konstantenausdruck nicht zulässig. Daher gibt es unter den angegebenen Bedingungen keine Lösung für Ihr Problem .Wir können das Problem jedoch lösen, indem wir zwei Einschränkungen biegen
nicht verwenden
static_assert
(verwenden Sie stattdessen andere Methoden, um eine Diagnose zur Kompilierungszeit zu erstellen) undIgnorieren Sie, dass der Komma-Operator "hässlich ist und einige Tools Warnungen darüber ausspucken". (Das Zeigen seiner Hässlichkeit ist eine unglückliche Folge der strengen Anforderungen der C ++ 11-
constexpr
Funktionen.)Dann können wir ein normales verwenden
assert
:In einem konstanten Auswertungskontext wird dies einen Compilerfehler wie ausgeben
error: call to non-'constexpr' function 'void __assert_fail(const char*, const char*, unsigned int, const char*)'
.quelle
Dies funktioniert für mich bei den meisten Compilern: https://godbolt.org/z/4nT2ub
Jetzt
static_assert
ist veraltet, daconstexpr
es kein undefiniertes Verhalten enthalten kann. Wenn Sie also den Array-Index-Compiler sprengen, meldet er den richtigen Fehler. Siehe hier .Problem ist
assert
. Es ist ein Makro, dessen Implementierung nicht definiert ist. Wenn der Compiler eine Funktion verwendet, die keine ist, schlägtconstexpr
sie fehl, aber wie Sie sehen können, haben 3 große Compiler damit kein Problem.quelle
C++11
dieser nicht auf gcc nur godbolt.org/z/DB2zL3/std:c++11
Flag überhaupt nicht und Clang lässt den Code zu, obwohl C ++ 14 erforderlich ist. Add-pedantic-errors
and Clang gibt die richtigen Fehler aus, die ein reiner C ++ 11-Compiler geben würde.c ++ 11 kann nicht so sein. Entweder
idx
konstant oder neinwäre schön, wenn jeweils eine Funktion vorhanden wäre.
Es könnte so sein, wenn eine Funktion erzwungen wird
quelle
idx
istconst
. Wenn dies nur ein C ++ - Standardvorschlag ist, sehe ich im Antwortbereich nicht, wie er dazu gehört.