se constexpr con static_assert in lambda, quale compilatore è corretto?


13

Quando vogliamo usare a static_assertin a if constexprdobbiamo rendere la condizione dipendente da alcuni parametri del template. È interessante notare che gcc e clang non sono d'accordo quando il codice è racchiuso in un lambda.

Il codice seguente viene compilato con gcc, ma clang attiva l'asserzione, anche se if constexprnon può essere vero.

#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>();
}

Esempio live qui .

Può essere facilmente risolto sostituendo False<T>con False<decltype(x)>.

Quindi la domanda è: quale compilatore è giusto? Suppongo che gcc sia corretto perché la condizione in static_assertdipende da T, ma non ne sono sicuro.


Questo fa la stessa domanda ma proviene dalla direzione opposta: stackoverflow.com/questions/59393908/… .
NathanOliver

1
@mfnx Impossibile riprodurre . Puoi condividere un esempio?
NathanOliver

2
Direi che entrambi hanno ragione (NDR mal formato): static_assert(False<int>, "AAA");equivale a static_assert(false, "AAA");all'interno della lambda.
Jarod42

2
@mfnx Hai modificato il valore della costante. L'uso dell'esempio del PO in cui la costante è f(std::integral_constant<int, 1>{});Wandbox non attiva l'asserzione: wandbox.org/permlink/UFYAmYwtt1ptsndr
NathanOliver

1
@NathanOliver Sì, hai ragione, scusa per il rumore. Sembra che gcc sia giusto poiché quel codice nel constexpr dovrebbe essere scartato se la costante> = 0;
mfnx,

Risposte:


1

Da [stmt.if] / 2 (sottolineatura mia)

Se l'istruzione if ha la forma if constexpr, il valore della condizione deve essere un'espressione costante convertita contestualmente di tipo bool; questo modulo è chiamato istruzione constexpr if. Se il valore della condizione convertita è falso, il primo sottostatement è un'istruzione scartata, altrimenti il ​​secondo sottostatement, se presente, è un'istruzione scartata. Durante l'istanza di un'entità modello racchiusa ([temp.pre]), se la condizione non dipende dal valore dopo la sua istanza, il sottostatement scartato (se presente) non viene istanziato.

Leggendo che si potrebbe pensare che l'asserzione statica verrebbe abbandonata, ma non è così.

L'asserzione statica viene attivata nella prima fase del modello perché il compilatore sa che è sempre falso.

Da [temp.res] / 8 (sottolineatura mia)

La validità di un modello può essere verificata prima di ogni istanza. [ Nota: sapere quali sono i nomi dei tipi consente di verificare in questo modo la sintassi di ogni modello. - nota di chiusura ] Il programma non è aggiornato, non è richiesta alcuna diagnostica se:

  • (8.1) non è possibile generare alcuna specializzazione valida per un modello o un sottostatement di un constexpr se l'istruzione all'interno di un modello e il modello non è istanziata , oppure

[...]

Sì, davvero, False<T>dipende da T. Il problema è che un lambda generico è esso stesso un modello e False<T>non dipende da alcun parametro di modello del lambda.

Se Tciò False<T>è falso, l'asserzione statica sarà sempre falsa, indipendentemente dall'argomento del modello inviato alla lambda.

Il compilatore può vedere che per ogni istanza del modello operator(), l'asserzione statica si innescherà sempre per l'attuale T. Quindi l'errore del compilatore.

Una soluzione per questo sarebbe dipendere da 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>{});
}

Esempio dal vivo


13

La regola abituale qui è [temp.res] / 8 :

Il programma non è corretto, non è richiesta alcuna diagnostica se: non è possibile generare una specializzazione valida per un modello o un sottostatement di un constexpr se l'istruzione all'interno di un modello e il modello non sono istanziati

Una volta che hai un'istanza foo<T>, ciò static_assertche hai non è più dipendente. Diventa static_assert(false)- per tutte le possibili istanze dell'operatore di chiamata del lambda generico f. È mal formato, non è necessaria la diagnostica. Clang diagnostica, gcc no. Sono corretti entrambi.

Nota che non importa che il static_assertqui venga scartato.

Può essere facilmente risolto sostituendo False<T>con False<decltype(x)>.

Ciò mantiene il static_assertdipendente all'interno del generico lambda, e ora entriamo in uno stato in cui si potrebbe ipoteticamente esserci una specializzazione valida, quindi non siamo più mal formati, ndr.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.