Tengo una estructura con muchos miembros del mismo tipo, como este
struct VariablePointers {
VariablePtr active;
VariablePtr wasactive;
VariablePtr filename;
};
El problema es que si olvido inicializar uno de los miembros de la estructura (por ejemplo wasactive
), así:
VariablePointers{activePtr, filename}
El compilador no se quejará de ello, pero tendré un objeto que está parcialmente inicializado. ¿Cómo puedo evitar este tipo de error? Podría agregar un constructor, pero duplicaría la lista de variables dos veces, ¡así que tengo que escribir todo esto tres veces!
Agregue también las respuestas de C ++ 11 , si hay una solución para C ++ 11 (actualmente estoy restringido a esa versión). Sin embargo, los estándares de idiomas más recientes también son bienvenidos.
c++
aggregate-initialization
Johannes Schaub - litb
fuente
fuente
-Wmissing-field-initializers
bandera de compilación.Respuestas:
Aquí hay un truco que desencadena un error de vinculador si falta un inicializador requerido:
Uso:
Salir:
Advertencias:
Foo
ser un agregado en absoluto.fuente
Foo
se declara, incluso si nunca llama al operador.Para clang y gcc puede compilar con
-Werror=missing-field-initializers
eso convierte la advertencia en los inicializadores de campo faltantes en un error. rayoEditar: para MSVC, parece que no se emite ninguna advertencia incluso a nivel
/Wall
, por lo que no creo que sea posible advertir sobre los inicializadores faltantes con este compilador. rayofuente
No es una solución elegante y práctica, supongo ... pero debería funcionar también con C ++ 11 y dar un error en tiempo de compilación (no tiempo de enlace).
La idea es agregar en su estructura un miembro adicional, en la última posición, de un tipo sin inicialización predeterminada (y que no puede inicializarse con un valor de tipo
VariablePtr
(o lo que sea el tipo de valores anteriores)Por ejemplo
De esta forma, se ve obligado a agregar todos los elementos en su lista de inicialización agregada, incluido el valor para inicializar explícitamente el último valor (un entero para
sentinel
, en el ejemplo) o se obtiene un error de "llamada al constructor eliminado de 'barra'".Entonces
compilar y
no lo hace
Lamentablemente también
no compila
- EDITAR -
Como señaló MSalters (gracias) hay un defecto (otro defecto) en mi ejemplo original: un
bar
valor podría inicializarse con unchar
valor (que es convertible aint
), por lo que funciona la siguiente inicializacióny esto puede ser muy confuso.
Para evitar este problema, he agregado el siguiente constructor de plantillas eliminado
entonces la
f4
declaración anterior da un error de compilación porque eld
valor es interceptado por el constructor de plantillas que se eliminafuente
foo f;
no se compile, pero tal vez sea más una característica que un defecto con este truco. Aceptará si no hay mejor propuesta que esta.enum
y nombrarinit_list_end
(o simplementelist_end
) un valor de esoenum
; pero la legibilidad agrega mucha mecanografía, por lo tanto, dado que el valor adicional es el punto débil de esta respuesta, no sé si es una buena idea.constexpr static int eol = 0;
en el encabezado debar
.test{a, b, c, eol}
me parece bastante legible.bar::eol
; es casi como pasar unenum
valor; pero no creo que sea importante: el núcleo de la respuesta es "agregue en su estructura un miembro adicional, en la última posición, de un tipo sin inicialización predeterminada"; labar
parte es solo un ejemplo trivial para mostrar que la solución funciona; el "tipo exacto sin inicialización predeterminada" debe depender de las circunstancias (en mi humilde opinión).Para CppCoreCheck, existe una regla para verificar exactamente eso, si todos los miembros se han inicializado y se puede convertir de advertencia en un error, que por lo general es de todo el programa.
Actualizar:
La regla que desea verificar es parte de typesafety
Type.6
:fuente
La forma más sencilla es no dar al tipo de miembros un constructor sin argumentos:
Otra opción: si sus miembros son constantes, debe inicializarlos a todos:
Si puede vivir con un miembro y un miembro ficticios, puede combinarlo con la idea de @ max66 de un centinela.
De cppreference https://en.cppreference.com/w/cpp/language/aggregate_initialization
Otra opción es tomar la idea centinela de max66 y agregar un poco de azúcar sintáctica para facilitar la lectura.
fuente
A
mover y cambia la semántica de copia (A
ya no es un agregado de valores, por así decirlo) :(1,2,3
objetos son efectivamente locales en el almacenamiento automático que quedan fuera del alcance cuando finaliza la función. Y hace que el tamaño de (A) 24 en lugar de 3 en un sistema con punteros de 64 bits (como x86-64).