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_sequence
s.
_v
deve ser inicializado na lista de inicialização, até C ++ 17. Talvez seja alterado algo em C ++ 20.int
membro nunca terão um comportamento indefinido " Gostaria de saber se o GCC não está fazendo isso é compatível, ou o contrário ...Respostas:
A inicialização padrão trivial foi proibida em um
constexpr
contexto 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.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.fonte
constexpr
e vasculhar a proposta vinculada;)