Legítimo para inicializar uma matriz em um construtor constexpr?

11

O código a seguir é legítimo?

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 aceita, mas GCC e MSVC rejeitam.

O erro do 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 esse tipo de código fosse bom, eu poderia fazer alguns usos de index_sequences.

Yongwei Wu
fonte
11
O Gcc10 também aceita.
songyuanyao
você pode transcrever o erro do MSVC?
max66 9/01
... e o GCC também.
Evg 9/01
11
@songyuanyao - g ++ 10 aceita compilar C ++ 20; recusa-o a compilar C ++ 17 ou mais antigo; parece que o ponto _vdeve ser inicializado na lista de inicialização, até C ++ 17. Talvez seja alterado algo em C ++ 20.
max66 9/01
2
@ Evg Isso é realmente interessante, porque pode sugerir que o Clang use sua "consciência" de que um objeto de duração de armazenamento estático é zerado para dizer "ok, esse objeto pode ter sido inicializado por padrão, mas as leituras de seu intmembro nunca terão um comportamento indefinido " Gostaria de saber se o GCC não está fazendo isso é compatível, ou o contrário ...
Lightness Races in Orbit em

Respostas:

13

A inicialização padrão trivial foi proibida em um constexprcontexto até C ++ 20 .

O motivo, suponho, é que é fácil "acidentalmente" ler as primitivas inicializadas por padrão, um ato que dá ao programa um comportamento indefinido e expressões com comportamento indefinido são diretamente proibidas de serem constexpr( ref ). O idioma foi estendido para que agora um compilador verifique se essa leitura ocorre e, se não ocorrer, a inicialização padrão deve ser aceita. É um pouco mais trabalhoso para o compilador, mas (como você viu!) Traz benefícios substanciais para o programador.

Este artigo propõe permitir a inicialização padrão para tipos construtivos trivialmente padrão em contextos constexpr, continuando a não permitir a invocação de comportamento indefinido. Em suma, desde que os valores não inicializados não sejam lidos, esses estados devem ser permitidos no constexpr nos cenários alocados na pilha e na pilha.

Desde o C ++ 20, é legal deixar v_"não inicializado" como você. Então você passou a atribuir todos os valores de seus elementos, o que é ótimo.

Raças de leveza em órbita
fonte
4
@ max66 Eu também! Tudo o que fiz foi digitalizar a lista de alterações do C ++ 20 na Wikipedia, encontrar algo relevante constexpre vasculhar a proposta vinculada;)
Lightness Races in Orbit em
3
A parte ruim é que há mais de 20 anos estou usando C ++. Se todos os dias eu aprendo algo novo ... ou sou um programador ruim ou o C ++ fica muito complicado.
max66 9/01
5
@ max66 É quase certamente o último. Além disso, o fato de continuar mudando fundamentalmente a cada dois anos também o torna um alvo em movimento rápido. Quem pode acompanhar isso ?! Até os compiladores não acompanham isso.
Lightness Races em órbita em
@ max66 Este artigo vem à mente: Lembre
Evg 9/01
@ Evg Oh, uau, esse papel passou por mim (IRONY). Spot on!
Lightness Races em órbita em