Legittimo per inizializzare un array in un costruttore constexpr?


11

Il seguente codice è legittimo?

template <int N>
class foo {
public:
    constexpr foo()
    {
        for (int i = 0; i < N; ++i) {
            v_[i] = i;
        }
    }

private:
    int v_[N];
};

constexpr foo<5> bar;

Clang lo accetta, ma GCC e MSVC lo respingono.

L'errore di GCC è:

main.cpp:15:18: error: 'constexpr foo<N>::foo() [with int N = 5]' called in a constant expression
   15 | constexpr foo<5> bar;
      |                  ^~~
main.cpp:4:15: note: 'constexpr foo<N>::foo() [with int N = 5]' is not usable as a 'constexpr' function because:
    4 |     constexpr foo()
      |               ^~~
main.cpp:4:15: error: member 'foo<5>::v_' must be initialized by mem-initializer in 'constexpr' constructor
main.cpp:12:9: note: declared here
   12 |     int v_[N];
      |         ^~

Se questo tipo di codice fosse OK, potrei tagliare parecchi usi di index_sequences.



potresti trascrivere l'errore da MSVC?
max66

... e anche GCC.
Evg

1
@songyuanyao - g ++ 10 lo accetta compilando C ++ 20; rifiuta di compilare C ++ 17 o precedenti; il punto sembra che _vdovrebbe essere inizializzato nell'elenco di inizializzazione, fino a C ++ 17. Forse è cambiato qualcosa in C ++ 20.
max66

2
@Evg Questo è davvero interessante, perché potrebbe suggerire che Clang usa la sua "consapevolezza" che un oggetto di durata della memoria statica viene azzerato per dire "okay, questo oggetto potrebbe essere stato inizializzato di default ma le letture dal suo intmembro non avranno mai un comportamento indefinito ". Mi chiedo se GCC non lo faccia, o viceversa ...
Razze di leggerezza in orbita

Risposte:


14

L'inizializzazione di default Trivial era proibita in un constexprcontesto fino a C ++ 20 .

Il motivo, immagino, è che è facile "accidentalmente" leggere da primitive inizializzate in modo predefinito, un atto che dà al programma un comportamento indefinito e che le espressioni con comportamento indefinito sono chiaramente vietate dall'essere constexpr( ref ). La lingua è stata estesa, quindi ora un compilatore deve verificare se tale lettura ha luogo e, in caso contrario, l'inizializzazione predefinita dovrebbe essere accettata. È un po 'più di lavoro per il compilatore, ma (come hai visto!) Ha notevoli vantaggi per il programmatore.

Questo documento propone di consentire l'inizializzazione di default per tipi costruibili banalmente di default in contesti di constexpr pur continuando a impedire l'invocazione di comportamenti indefiniti. In breve, fintanto che i valori non inizializzati non vengono letti, tali stati dovrebbero essere consentiti in constexpr in entrambi gli scenari allocati in heap e stack.

Dal C ++ 20, è legale lasciare v_"non inizializzato" come hai fatto tu. Quindi hai continuato ad assegnare tutti i suoi valori agli elementi, il che è fantastico.


4
@ max66 Anche a me! Tutto quello che ho fatto è stato scansionare l'elenco delle modifiche di C ++ 20 su Wikipedia, trovare qualcosa di rilevante constexpre sfogliare la proposta collegata;)
Lightness Races in Orbit

3
La parte negativa è che sono più di 20 anni che uso C ++. Se ogni giorno imparo qualcosa di nuovo ... o sono un cattivo programmatore o C ++ diventa troppo complicato.
max66

5
@ max66 È quasi certamente quest'ultimo. Inoltre, il fatto che continui a cambiare radicalmente ogni due anni lo rende un obiettivo in rapido movimento. Chi può tenere il passo con quello ?! Anche i compilatori non stanno al passo con quello.
Corse di leggerezza in orbita

@ max66 Questo documento mi viene in mente: ricorda la Vasa!
Evg

@Evg Oh, wow, quel foglio mi aveva passato (IRONY). Spot on!
Corse di leggerezza in orbita
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.