uniones tipos enumerados enum crear c++ templates enums static const

c++ - tipos - En un rasgo de tipo, ¿por qué las personas usan enumeración en lugar de constantes estáticas para el valor?



tipos enumerados en c (5)

Por ejemplo, así es como lo escribiría, y compila y funciona bien:

template<typename T> struct is_pointer<T*> { static const bool value = true; }

Entonces, ¿por qué algunas personas escriben lo menos obvio?

template<typename T> struct is_pointer<T*> { enum { value = true }; }

¿en lugar? ¿Es solo porque la variable static const usa un byte de memoria, mientras que la enum no lo hace?


¿Es solo porque la variable static const usa un byte de memoria, mientras que la enum no lo hace?

Sí, esa es la razón.

static const bool value = true;

ocuparía la memoria, mientras

enum { value = true };

no lo hace


Algunas personas escriben la enum menos obvia en lugar de static bool const porque no se dan cuenta de que hay otros cambios que deberían hacer.

C ++ requiere que se defina el objeto si se necesita su dirección, por ejemplo, si se pasa a esta función foo :

void foo(bool const &);

Sin embargo, resolver el problema definiendo el objeto en realidad no es la solución correcta para este problema. Aquí hay algunas alternativas:

  1. Los objetos pequeños no deben ser pasados ​​por referencia . El cambio debe ser eliminar const & de la firma de función, no agregar una definición para el objeto.

  2. Donde no se puede cambiar la firma de la función, se puede crear un temporal explícitamente en la llamada: foo( bool { Cls::mbr } )

  3. Sin embargo, esto es información de tiempo de compilación! Por lo tanto, foo debería ser una plantilla con una sobrecarga de T y T* , o estar especializado en bool .

Esta tercera solución tiene la ventaja de eliminar una verificación de tiempo de ejecución innecesaria (que se espera que optimice el compilador) y también permitir que el puntero y el no puntero se manejen de manera independiente, posiblemente haciendo que el código sea más claro.


Sí, tiene razón: enum { value = true }; No ocupa ningún recuerdo.

Además, antes de C ++ 11, era prácticamente la única forma de lograrlo: static const bool value = true; Solo es legal en una definición de clase desde C ++ 11 en adelante. Aunque podría constexpr un constexpr .


También es otro símbolo en cada archivo de objeto que lo incluye, sin ningún beneficio. Si usa el plegado de símbolos (--gc-secciones), se quedará sin secciones separables e inflará su binario.


Una diferencia notable es el hecho de que el siguiente código compila y enlaza:

template<typename> struct is_pointer { }; template<typename T> struct is_pointer<T*> { enum { value = true }; }; void f(const bool &b) { } int main() { f(is_pointer<void*>::value); }

Lo siguiente no funciona en su lugar (obtienes una referencia a value no definida ):

template<typename> struct is_pointer { }; template<typename T> struct is_pointer<T*> { static const bool value = true; }; void f(const bool &b) { } int main() { f(is_pointer<void*>::value); }

Por supuesto, no funciona a menos que agregue en algún lugar las siguientes líneas:

template<typename T> const bool is_pointer<T*>::value;

Esto se debe a [class.static.data]/3 (énfasis mío):

Si un miembro de datos estáticos const no en línea no volátil es de tipo integral o de enumeración, su declaración en la definición de clase puede especificar un inicializador de refuerzo o igual en el que cada cláusula de inicialización que es una expresión-asignación es una constante expresión ([expr.const]). El miembro aún se definirá en un ámbito de espacio de nombres si se usa odr-used ([basic.def.odr]) en el programa y la definición del ámbito de espacio de nombres no contendrá un inicializador. [...]

En otros términos, static const bool value = true; es una declaración, no una definición y no se puede usar el value .
Por otro lado, de acuerdo con [dcl.enum/1] (énfasis mío):

Una enumeración es un tipo distinto con constantes nombradas .

Esas constantes nombradas se pueden referenciar como se muestra en el ejemplo anterior.

Como nota al margen, algo similar se aplica si usa miembros de datos constexpr static en C ++ constexpr :

template<typename T> struct is_pointer<T*> { static constexpr bool value = true; };

Esto no funciona tan bien y así es como descubrí las diferencias sutiles entre ellos.

Encontré ayuda aquí para que obtuviera algunos buenos consejos de la respuesta que me dieron.
Las referencias a la norma son un punto a favor para explicar mejor lo que sucede debajo del capó.

Tenga en cuenta que una declaración de miembro de datos constexpr static como la anterior también es una definición desde C ++ 17. Por lo tanto, no tendrá que definirlo más y podrá usarlo directamente en su lugar.

Como se mencionó en los comentarios (gracias a @Yakk que confirmó esto), también estoy tratando de explicar cómo sucede que las constantes nombradas anteriormente se unen a una referencia constante.

[expr.const/3] introduce la expresión constante constante y menciona las enumas sin ámbito diciendo que se convierte implícitamente en un valor predefinido .
[dcl.init.ref/5] y [class.temporary/2] hacen el resto, ya que gobiernan la vinculación de referencia y los temporales.