Wenn constexpr mit static_assert in Lambda, welcher Compiler ist korrekt?

13

Wenn wir a static_assertin a verwenden möchten, if constexprmüssen wir die Bedingung von einem Vorlagenparameter abhängig machen. Interessanterweise stimmen gcc und clang nicht überein, wenn der Code in ein Lambda eingewickelt ist.

Der folgende Code wird mit gcc kompiliert, aber clang löst die Bestätigung aus, auch wenn das if constexprnicht wahr sein kann.

#include <utility>

template<typename T> constexpr std::false_type False;

template<typename T>
void foo() {

    auto f = [](auto x) {
        constexpr int val = decltype(x)::value;
        if constexpr(val < 0) {
            static_assert(False<T>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

int main() {
    foo<int>();
}

Live Beispiel hier .

Es kann leicht durch Ersetzen False<T>durch behoben werden False<decltype(x)>.

Die Frage ist also: Welcher Compiler ist richtig? Ich würde annehmen, dass gcc korrekt ist, da die Bedingung in der static_assertabhängig ist T, aber ich bin nicht sicher.

florestan
quelle
Dies stellt die gleiche Frage, kommt jedoch aus der entgegengesetzten Richtung: stackoverflow.com/questions/59393908/… .
NathanOliver
1
@mfnx Kann nicht reproduzieren . Können Sie ein Beispiel nennen?
NathanOliver
2
Ich würde sagen, beide haben Recht (schlecht geformter NDR): static_assert(False<int>, "AAA");entspricht dem static_assert(false, "AAA");Inneren des Lambda.
Jarod42
2
@mfnx Sie haben den Wert der Konstante geändert. Wenn Sie das Beispiel des OP verwenden, in dem die Konstante f(std::integral_constant<int, 1>{});Wandbox lautet, wird die Bestätigung nicht ausgelöst: wandbox.org/permlink/UFYAmYwtt1ptsndr
NathanOliver
1
@ NathanOliver Ja, du hast recht, entschuldige den Lärm. Scheint, gcc ist genau richtig, da dieser Code im constexpr verworfen werden sollte, wenn die Konstante> = 0 ist;
mfnx

Antworten:

1

Aus [stmt.if] / 2 (Schwerpunkt Mine)

Wenn die if-Anweisung die Form if constexpr hat, muss der Wert der Bedingung ein kontextkonvertierter konstanter Ausdruck vom Typ bool sein. Diese Form wird als constexpr if-Anweisung bezeichnet. Wenn der Wert der konvertierten Bedingung falsch ist, ist die erste Unteranweisung eine verworfene Anweisung, andernfalls ist die zweite Unteranweisung, falls vorhanden, eine verworfene Anweisung. Wenn während der Instanziierung einer einschließenden Vorlagenentität ([temp.pre]) die Bedingung nach ihrer Instanziierung nicht wertabhängig ist, wird die verworfene Unteranweisung (falls vorhanden) nicht instanziiert.

Wenn man liest, dass man denken würde, die statische Behauptung würde fallengelassen, aber das ist nicht der Fall.

Die statische Zusicherung wird in der ersten Phase der Vorlage ausgelöst, da der Compiler weiß, dass sie immer falsch ist.

Aus [temp.res] / 8 (Schwerpunkt Mine)

Die Gültigkeit einer Vorlage kann vor jeder Instanziierung überprüft werden. [ Hinweis: Wenn Sie wissen, welche Namen Typnamen sind, kann die Syntax jeder Vorlage auf diese Weise überprüft werden. - Endnote ] Das Programm ist schlecht geformt, keine Diagnose erforderlich, wenn:

  • (8.1) Für eine Vorlage oder eine Unteraussage eines constexpr kann keine gültige Spezialisierung generiert werden, wenn die Anweisung innerhalb einer Vorlage und die Vorlage nicht instanziiert ist , oder

[...]

Ja, das False<T>hängt davon ab T. Das Problem ist, dass ein generisches Lambda selbst eine Vorlage ist und False<T>nicht von einem Vorlagenparameter des Lambda abhängig ist.

Für a T, False<T>das falsch ist, ist die statische Zusicherung immer falsch, unabhängig davon, welches Vorlagenargument an das Lambda gesendet wird.

Der Compiler kann sehen, dass bei jeder Instanziierung der Vorlage operator()die statische Zusicherung immer für das aktuelle T ausgelöst wird. Daher der Compilerfehler.

Eine Lösung hierfür wäre x:

template<typename T>
void foo() {

    auto f = [](auto x) {
        if constexpr(x < 0) {
            static_assert(False<decltype(x)>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

Live Beispiel

Guillaume Racicot
quelle
13

Die übliche Regel hier ist [temp.res] / 8 :

Das Programm ist fehlerhaft, keine Diagnose erforderlich, wenn: Für eine Vorlage oder eine Unteranweisung einer constexpr if-Anweisung innerhalb einer Vorlage keine gültige Spezialisierung generiert werden kann und die Vorlage nicht instanziiert wird

Sobald Sie instanziieren foo<T>, ist das, was static_assertSie haben, nicht mehr abhängig. Es wird static_assert(false)- für alle möglichen Instanziierungen des Anrufbetreibers des generischen Lambda f. Das ist schlecht geformt, keine Diagnose erforderlich. Clang diagnostiziert, gcc nicht. Beide sind richtig.

Beachten Sie, dass es nicht , dass das keine Rolle static_asserthier wird verworfen.

Es kann leicht durch Ersetzen False<T>durch behoben werden False<decltype(x)>.

Dies hält die static_assertAbhängigkeit innerhalb des generischen Lambda, und jetzt kommen wir in einen Zustand, in dem es hypothetisch eine gültige Spezialisierung geben könnte, so dass wir nicht länger schlecht geformt sind, ndr.

Barry
quelle