Estoy tratando de encontrar una manera conveniente de inicializar las estructuras C ++ 'pod'. Ahora, considere la siguiente estructura:
struct FooBar {
int foo;
float bar;
};
// just to make all examples work in C and C++:
typedef struct FooBar FooBar;
Si quiero iniciar esto convenientemente en C (!), Simplemente podría escribir:
/* A */ FooBar fb = { .foo = 12, .bar = 3.4 }; // illegal C++, legal C
Tenga en cuenta que quiero evitar explícitamente la siguiente notación, porque me parece que me rompen el cuello si cambio algo en la estructura en el futuro:
/* B */ FooBar fb = { 12, 3.4 }; // legal C++, legal C, bad style?
Para lograr lo mismo (o al menos similar) en C ++ como en el /* A */
ejemplo, tendría que implementar un constructor idiota:
FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar) {}
// ->
/* C */ FooBar fb(12, 3.4);
Lo cual es bueno para hervir agua, pero no es adecuado para personas perezosas (la pereza es algo bueno, ¿verdad?). Además, es casi tan malo como el /* B */
ejemplo, ya que no establece explícitamente qué valor va a qué miembro.
Entonces, mi pregunta es básicamente ¿cómo puedo lograr algo similar /* A */
o mejor en C ++? Alternativamente, estaría de acuerdo con una explicación de por qué no debería querer hacer esto (es decir, por qué mi paradigma mental es malo).
EDITAR
Por conveniente , quiero decir también mantenible y no redundante .
fuente
Respuestas:
Las inicializaciones designadas serán compatibles con c ++ 2a, pero no tiene que esperar, ya que oficialmente son compatibles con GCC, Clang y MSVC.
GCC Demo MSVC Demo
fuente
Como
style A
no está permitido en C ++ y no quieresstyle B
, ¿qué tal si usasstyle BX
:Al menos ayuda en alguna medida.
fuente
foo
ybar
en el futuro. C todavía inicializaría los campos que queremos, pero C ++ no lo haría. Y este es el punto de la pregunta: cómo lograr el mismo resultado en C ++. Quiero decir, Python hace esto con argumentos con nombre, C - con campos "con nombre", y C ++ también debería tener algo, espero.explicit FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar)
. Tenga en cuenta la palabra clave explícita . Incluso romper el estándar es mejor en lo que respecta a la seguridad. En Clang: -Wno-c99-extensionesPodrías usar una lambda:
Se puede encontrar más información sobre este idioma en el blog de Herb Sutter .
fuente
fb.XXX = YYY
.Extraiga los contenidos en funciones que los describen (refactorización básica):
Sé que el estilo es muy cercano al que no quería usar, pero permite reemplazar más fácilmente los valores constantes y también explicarlos (por lo tanto, no es necesario editar comentarios), si alguna vez cambian, eso es.
Otra cosa que podrías hacer (ya que eres flojo) es hacer que el constructor esté en línea, para que no tengas que escribir tanto (eliminando "Foobar ::" y el tiempo dedicado a cambiar entre el archivo h y cpp):
fuente
Su pregunta es algo difícil porque incluso la función:
puede ser llamado como:
debido a las reglas de promoción y conversiones para los tipos numéricos integrados. (C nunca ha sido muy fuertemente tipado)
En C ++, lo que quiere se puede lograr, aunque con la ayuda de plantillas y afirmaciones estáticas:
En C, puede nombrar los parámetros, pero nunca llegará más lejos.
Por otro lado, si todo lo que quieres son parámetros nombrados, entonces escribes mucho código engorroso:
Y puede agregarle protección de promoción tipo si lo desea.
fuente
Las interfaces de C ++ de muchos compiladores (incluidos GCC y clang) entienden la sintaxis del inicializador de C. Si puedes, simplemente usa ese método.
fuente
private: FooBar(float x, int y) {};
Otra forma más en C ++ es
fuente
inline
!Opción D:
FooBar FooBarMake(int foo, float bar)
Legal C, legal C ++. Fácilmente optimizable para POD. Por supuesto, no hay argumentos con nombre, pero esto es como todos los C ++. Si desea argumentos con nombre, el objetivo C debería ser una mejor opción.
Opción E:
Legal C, legal C ++. Argumentos nombrados.
fuente
FooBar fb = {};
en C ++, inicializa por defecto todos los miembros de la estructura.Sé que esta pregunta es antigua, pero hay una manera de resolver esto hasta que C ++ 20 finalmente traiga esta característica de C a C ++. Lo que puede hacer para resolver esto es usar macros de preprocesador con static_asserts para verificar que su inicialización sea válida. (Sé que las macros son generalmente malas, pero aquí no veo otra forma). Vea el código de ejemplo a continuación:
Luego, cuando tenga una estructura con atributos const, puede hacer esto:
Es un poco incómodo, porque necesita macros para cada número posible de atributos y necesita repetir el tipo y el nombre de su instancia en la llamada de macro. Además, no puede usar la macro en una declaración de devolución, porque las afirmaciones vienen después de la inicialización.
Pero sí resuelve su problema: cuando cambia la estructura, la llamada fallará en tiempo de compilación.
Si usa C ++ 17, incluso puede hacer que estas macros sean más estrictas al forzar los mismos tipos, por ejemplo:
fuente
La forma
/* B */
está bien en C ++, también C ++ 0x va a extender la sintaxis, por lo que también es útil para contenedores C ++. No entiendo por qué lo llamas mal estilo?Si desea indicar parámetros con nombres, puede usar la biblioteca de parámetros de impulso , pero puede confundir a alguien que no esté familiarizado con ellos.
Reordenar los miembros de la estructura es como reordenar los parámetros de la función, tal refactorización puede causar problemas si no lo hace con mucho cuidado.
fuente
¿Qué pasa con esta sintaxis?
Acabo de probar en un Microsoft Visual C ++ 2015 y en g ++ 6.0.2. Trabajando bien
También puede hacer una macro específica si desea evitar duplicar el nombre de la variable.
fuente
clang++
3.5.0-10 con-Weverything -std=c++1z
parece confirmar eso. Pero no se ve bien. ¿Sabes dónde el estándar confirma que esto es válido C ++?ABCD abc = { abc.b = 7, abc.a = 5 };
.Para mí, la forma más perezosa de permitir la inicialización en línea es usar esta macro.
Esa macro crea atributo y método de autorreferencia.
fuente