Static constexpr int vs enumeración antigua: ¿cuándo y por qué?

4 minutos de lectura

avatar de usuario de skypjack
skypjack

Esta es quizás una pregunta básica, pero no puedo ver la respuesta por mí mismo en este momento.

Considere el siguiente código:

template<bool b>
struct T {
    static constexpr int value = (b ? 42 : 0);
};

template<bool b>
struct U {
    enum { value = (b ? 42 : 0) };
};

int main() {
    static_assert(T<true>::value == 42, "!");
    static_assert(T<false>::value == 0, "!");
    static_assert(U<true>::value == 42, "!");
    static_assert(U<false>::value == 0, "!");
}

Estoy acostumbrado a usar estructuras como Tpero más de una vez he visto estructuras como U utilizado para el mismo propósito (principalmente definición de rasgos).

Por lo que puedo ver, ambos se resuelven en tiempo de compilación y resuelven casi el mismo problema, pero me parece que T es mucho más legible que U (bueno, lo sé, mi opinión personal).

Mi pregunta es bastante simple: ¿hay alguna razón técnica por la cual una solución sea mejor que la otra?
Es más, ¿existe algún caso para el cual alguno de ellos no sea una solución viable?

  • Si estuviera usando algo más que integrales, entonces TENDRÁ que usar constexpr.

    – Edward Extraño

    16 mayo 2016 a las 17:48

  • @CrazyEddie ¡Esto es bastante obvio! El ejemplo no está ahí para un error. 🙂

    – skypjack

    16 mayo 2016 a las 18:17

  • Me gustaría saber las razones de los votantes negativos y los que votaron para cerrar la pregunta como basada principalmente en la opinión, con una respuesta tan técnica publicada por @SergeyA (como se señaló correctamente en la pregunta por sus partes técnicas, de hecho) . Bastante gracioso.

    – skypjack

    16 mayo 2016 a las 19:18


Avatar de usuario de SergeyA
sergeya

Tenga en cuenta que la respuesta a continuación no se aplica a C++ 17 y versiones posteriores.

No habrá una diferencia notable para las constantes integrales cuando se usen así.

Sin embargo, enum en realidad es mejor, porque es una verdadera constante con nombre. constexpr constante integral es un objeto que puede ser, por ejemplo, ODR-usado – y eso daría lugar a errores de vinculación.

#include <iostream>

struct T {
    static constexpr int i = 42;
    enum : int {x = 42};
};

void check(const int& z) {
    std::cout << "Check: " << z << "\n";
}

int main() {
    // check(T::i); // Uncommenting this will lead to link error
    check(T::x);
}

Cuándo check(T::i) no está comentado, el programa no se puede vincular:

/tmp/ccZoETx7.o: En función `main‘: ccc.cpp🙁.text+0x45): referencia indefinida a `T::icollect2: error: ld devuelto 1 estado de salida

Sin embargo, el verdadero enum siempre funciona

  • Puede solucionar el problema creando una instancia i en un archivo cpp, pero eso le permite tomar la dirección de &. Tomar la dirección de un valor no tiene sentido. Las enumeraciones no permiten esto.

    – Pato mugido

    16 mayo 2016 a las 20:19

  • @BenCollins Puedes ver 3.2p3 o cualquier otro sitio como Éste.

    – skypjack

    18 de mayo de 2016 a las 5:49

  • @gzlbg inline ¿variable? Nunca he oído hablar de ellos. ¿Tienes un enlace? Gracias.

    – skypjack

    18 mayo 2016 a las 9:50

  • @skypjack N4424

    – Barry

    18 mayo 2016 a las 14:29

  • @SergeyA, “Si un programa contiene una violación de una regla para la cual no se requiere diagnóstico, esta Norma Internacional no establece ningún requisito sobre las implementaciones con respecto a ese programa”.

    – ixSci

    19 mayo 2016 a las 13:43


Avatar de usuario de R Sahu
R Sahú

Sospecho que es código heredado.

enum { value = (b ? 42 : 0) };

es un código válido tanto en C++03 como en C++11.

static constexpr int value = (b ? 42 : 0);

solo es válido en C++11.

Es más, ¿existe algún caso para el cual alguno de ellos no sea una solución viable?

Ambas son soluciones viables en C++11. La elección de cuál usar depende de un equipo. Va a ser una cuestión de decisión política.

Como indica la respuesta de SergeyA, enum son verdaderas constantes. No puede usar ODR. Puede usar ODR constexpr. Dependiendo de cuál de estos es deseable para su aplicación, puede decidir si usar enums o constexprs.

  • Tiene sentido y sospecho casi lo mismo. He preguntado en SO con respecto a oscuras razones técnicas porque no estoy seguro de eso, eso es todo. 🙂

    – skypjack

    16 mayo 2016 a las 17:38

  • @skypjack, esperemos que alguien tenga más información.

    – R Sahu

    16 mayo 2016 a las 17:41

  • Hay un beneficio en el uso de enumeración, vea mi respuesta.

    – SergeyA

    16 mayo 2016 a las 17:41

  • @RSahu, dudo que haya un caso en el que alguien quiere a ODR-use un constexpr. Esto generalmente no es deseado.

    – SergeyA

    16 mayo 2016 a las 17:53

  • @SergeyA, no estoy dispuesto a apostar en contra de eso. He visto a los desarrolladores hacer algunas locuras.

    – R Sahu

    16 mayo 2016 a las 17:55


La respuesta actualmente aceptada por SergeyA ya no es válida a partir de C++17 (Definiciones y ODR).

Cada declaración es una definición, excepto por lo siguiente:

  • (Obsoleto) Declaración de ámbito de espacio de nombres de un miembro de datos estáticos que se definió dentro de la clase con el especificador constexpr
struct S {
    static constexpr int x = 42; // implicitly inline, defines S::x
};
constexpr int S::x; // declares S::x, not a redefinition

Por lo tanto, a partir de C ++ 17, usaría la definición estática constexpr que es más expresiva que la enumeración.

¿Ha sido útil esta solución?