¿Es legítimo el siguiente código?
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 acepta, pero GCC y MSVC lo rechazan.
El error de GCC es:
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];
| ^~
Si este tipo de código estuviera bien, podría cortar bastantes usos de index_sequence
s.
_v
debería inicializarse en la lista de inicialización, hasta C ++ 17. Quizás haya cambiado algo en C ++ 20.int
miembro nunca tendrán un comportamiento indefinido ". Me pregunto si GCC no hace eso es compatible, o al revés ...Respuestas:
La inicialización trivial predeterminada estaba prohibida en un
constexpr
contexto hasta C ++ 20 .Supongo que la razón es que es fácil leer "accidentalmente" de primitivas inicializadas por defecto, un acto que le da a su programa un comportamiento indefinido, y las expresiones con comportamiento indefinido están prohibidas
constexpr
( ref ). Sin embargo, el lenguaje se ha extendido de modo que ahora un compilador debe verificar si dicha lectura se lleva a cabo y, si no es así, se debe aceptar la inicialización predeterminada. Es un poco más de trabajo para el compilador, pero (¡como has visto!) Tiene beneficios sustanciales para el programador.Desde C ++ 20, es legal dejar
v_
"sin inicializar" como lo ha hecho. Luego ha asignado todos los valores de sus elementos, lo cual es genial.fuente
constexpr
y leer la propuesta vinculada;)