Espacios de nombres
Variantes
Acciones

Expresiones constantes

De cppreference.com
< cpp‎ | language
 
 
 
Expresiones
General
categorías de valores (lvalue, rvalue, xvalue)
orden de evaluación (puntos de secuencia)
expresiones constantes
Operadores
Operadores de asignación: a=b, a+=b, a-=b, a*=b, a/=b, a%=b, a&=b, a|=b, a^=b, a<<=b, a>>=b
Operadores aritméticos: +a, -a, a+b, a-b, a*b, a/b, a%b, ~a, a&b, a|b, a^b, a<<b, a>>b
Incremento y decremento: ++a, --a, a++, a--
Operadores lógicos: a||b, a&&b, !a
Operadores de comparación: a==b, a!=b, a<b, a>b, a<=b, a>=b, a<=>b(C++20)
Operadores de acceso a miembro: a[b], *a, &a, a->b, a.b, a->*b, a.*b
Otros operadores: a(...), a,b, a?b:c
sizeof
alignof (C++11)
expresión new
expresión delete
typeid
 

Se define como una expresión que se puede evaluar en tiempo de compilación.

Dichas expresiones se pueden usar como argumentos de plantilla sin tipo, tamaños de matriz y en otros contextos que requieren expresiones constantes, por ejemplo:

int n = 1;
std::array<int, n> a1; // error: n no es una expresión constante
const int cn = 2;
std::array<int, cn> a2; // OK: cn es una expresión constante

Contenido

[editar] Expresiones constantes principales

Una expresión constante principal es cualquier expresión cuya evaluación no evalúe alguna de las siguientes:

  1. el puntero this, excepto en una función constexpr o constructor constexpr que se está evaluando como parte de la expresión
  2. una expresión de llamada a función que llama a una función (o constructor) que no está declarado constexpr
    constexpr int n = std::numeric_limits<int>::max(); // OK: max() es constexpr
    constexpr int m = std::time(nullptr); // Error: std::time() no es constexpr
  3. una llamada a función a una función constexpr que está declarada, pero no definida
  4. una llamada a función a instancia de plantilla de función/constructor constexpr donde la instanciación falla al satisfacer los requerimientos de una función/constructor constexpr.
  5. una expresión que excedería los límites definidos por la implementación.
  6. una expresión cuya evaluación conduce a cualquier forma de comportamiento indeterminado del núcleo del lenguaje (incluyendo desbordamiento de enteros, división por cero, aritmética de punteros fueras de los límites de una matriz, etc). No se especifica si se detecta un comportamiento indeterminado de la biblioteca estándar.
    constexpr double d1 = 2.0/1.0; // OK
    constexpr double d2 = 2.0/0.0; // Error: no definido
    constexpr int n = std::numeric_limits<int>::max() + 1; // Error: desbordamiento
    int x, y, z[30];
    constexpr auto e1 = &y - &x; // Error: sin definir
    constexpr auto e2 = &z[20] - &z[3]; // OK
    constexpr std::bitset<2> a; 
    constexpr bool b = a[2]; // Comportamiento indeterminado, pero no se especifica si se detecta
  7. (hasta C++17) una expresión lambda
  8. una conversión implícita de lvalue a rvalue, a menos que el lvalue ...
    1. tiene tipo integral o enumeración y se refiere a un objeto constante no volátil completo, que se inicializa con una expresión constante
      int main() {
          const std::size_t tabsize = 50;
          int tab[tabsize]; // OK: tabsize es una expresión constante
       
          std::size_t n = 50;
          const std::size_t sz = n;
          int tab2[sz]; // error: sz no es una expresión constante
                        // porque sz no se inicia con una expresión constante
      }
    2. es un glvalue no volátil que se refiere a un elemento de un literal cadena
    3. tiene tipo literal y se refiere a un objeto no volátil definido con constexpr o un objeto parámetro de plantilla (desde C++20) o a su subobjeto inmutable
    4. tiene tipo literal y se refiere a un objeto no volátil cuyo tiempo de vida comienza con la evaluación de esta expresión
  9. una conversión implícita de lvalue a rvalue o modificación aplicada al miembro no activo de una union o sus subobjetos (incluso si comparte una secuencia inicial común con el miembro activo)
  10. una invocación de un constructor de copia/movimiento definido implícitamente o operador de asignación de copia/movimiento para una unión cuyo miembro activo (si corresponde) es mutable, a no ser que el tiempo de vida del objeto comience dentro de la evaluación de esta expresión.
  11. (desde C++17) (hasta C++20) una expresión de asignación o la invocación de un operador de asignación sobrecargado que podría cambiar el miembro activo de una unión
  12. una expresión id referente a una variable o miembro de datos del tipo referenciado, a no ser que se incializara con una expresión constante o su tiempo de vida comience dentro de la evaluación de esta expresión
  13. (desde C++20) una contrato revisado cuyo predicado se evalúa a false
  14. conversión desde void* a cualquier tipo de puntero a objeto
  15. dynamic_cast
  16. reinterpret_cast
  17. llamada a un pseudodestructor
  18. (hasta C++14) un operador de incremento o decremento
  19. (desde C++14) modificación de un objeto, a menos que el objeto tenga tipo literal no volátil y su tiempo de vida comience en el interior de la evaluación de la expresión
    constexpr int incr(int& n) {
      return ++n;
    }
    constexpr int g(int k) {
      constexpr int x = incr(k); // error: incr(k) no es una expresión constante
                                 // principal porque el tiempo de vida de k
                                 // comienza fuera de la expresión incr(k)
      return x;
    }
    constexpr int h(int k) {
      int x = incr(k); // OK: no se requiere que x se inicie con
                       // una expresión constante principal
      return x;
    }
    constexpr int y = h(1); // OK: y se inicia con el valor 2
                            // h(1) es una expresión constante principal porque
                            // el tiempo de vida de k comienza dentro de la expresión h(1)
  20. una expresión typeid aplicada a un valor glvalue de tipo polimórfico
  21. una expresión new o delete
  22. (desde C++20) una comparación de tres vías comparando punteros que no apuntan al mismo objeto completo o a cualquier subobjeto del mismo
  23. un operador de igualdad o relacional cuando el resultado es inespecífico
  24. (hasta C++14) un operador de asignación o asiganción compuesta
  25. una expresión throw
  26. (desde C++20) una expresión dynamic_cast o typeid que podría lanzar una excepción
  27. dentro de una expresión lambda, una referencia a this o a una variable definida fuera de este lambda, si esa referencia fuera de uso odr
    void g() {
      const int n=0;
      constexpr int j=*&n; // OK: fuera de una expresión lambda
      [=]{ constexpr int i=n;  // OK: 'n' no es de uso odr y no se captura aquí
           constexpr int j=*&n;// mal formado: '&n' sería de uso odr de 'n'.
         };
    }

    tenga en cuenta que si el uso ODR tiene lugar en una llamada a función a una finalización, no se refiere a this o a una variable que lo encierra, ya que accede al miembro de datos del cierre en su lugar

    // OK: 'v' y 'm' son de uso ODR pero no están en expresión contante
    // dentro del lambda anidado
    auto monad = [](auto v){return [=]{return v;};};
    auto bind = [](auto m){return [=](auto fvm){return fvm(m());};};
    // OK a tener capturas de objetos automáticos creados durante la evaluación de la expresión constante.
    static_assert(bind(monad(2))(monad)() == monad(2)());
    (desde C++17)

Nota: el simple hecho de ser una expresión constante principal no tiene ningún significado semántico directo: una expresión debe ser uno de los siguientes subconjuntos que se usarán en ciertos contextos:

[editar] Expresión constante entera

La expresión constante entera es una expresión de tipo entero o enumerado sin ámbito convertida implícitamente a prvalue, donde la expresión convertida es una expresión constante principal. Si se usa una expresión de tipo clase donde se espera una expresión constante entera, la expresión se convierte implícitamente según el contexto a un tipo entero o enumerado sin ámbito.

solamente las expresiones constante enteras se pueden usar como límites de matriz, las dimensiones en expresiones new distintas de la primera (hasta C++14), longitud de campos de bits, inicializadores de enumerados cuando es tipo subyacente no está establecido, constantes de puntero nulo (hasta C++14), y alineaciones.

[editar] Expresiones constantes convertidas

Una expresión constante convertida de tipo T es una expresión convertida implícitamente al tipo T, donde la expresión convertida es una expresión constante, y la secuencia de conversión implícita solamente contiene:

  • conversiones constexpr definidas por usuario (por lo que se puede usar una clase donde se espera un tipo entero)
  • conversiones de lvalue a rvalue
  • promociones enteras
  • conversiones enteras no reducidas
  • conversiones de matriz a puntero
  • conversiones de función a puntero
  • conversiones de puntero a función (puntero a función noexcept a puntero a función)
  • conversiones de calificación
  • conversiones de puntero nulo desde std::nullptr_t
  • conversiones puntero miembro nulo desde std::nullptr_t
(desde C++17)
  • Y si tiene lugar algún enlace de referencia, se enlaza directamente (no uno que construya un objeto temporal)
Solamente las expresiones constantes convertidas se pueden usar como expresiones case, inicializadores de enumerados cuando es tipo subyacente esta establecido, límites de matriz, las dimensiones en una expresión new distintas de la primera (desde C++14), y como enteros y enumerado (hasta C++17) en argumentos de plantilla sin tipo.

Una expresión constante convertida según el contexto de tipo bool es una expresión, convertida contextualmente a bool, donde la expresión convertida es una expresión constante y la secuencia de conversión contiene solamente las conversiones anteriores. Dichas expresiones se pueden usar en especificaciones noexcept y declaraciones static assert.

[editar] Expresión constante

Una expresión constante es

  • una expresión constante principal glvalue que se refiere a
  • un objeto con duración de almacenamiento estático que no es temporal, o
  • un objeto con una duración de almacenamiento estático que es temporal, pero cuyo valor satisface las restricciones para los prvalues definidos a continuación, o
  • una función
  • una expresión constante principal prvalue cuyo valor satisface las restricciones siguientes:
  • si el valor es un objeto de tipo clase, cada miembro de datos no estático de tipo referencia se refiere a una entidad que satisface las restricciones para glvalues anteriores
  • si el valor es de tipo puntero, se mantiene
  • la dirección de un objeto con duración de almacenamiento estático
  • la dirección más allá del final de un objeto con duración de almacenamiento estático
  • la dirección de una función
  • el valor del un puntero nulo
  • si el valor es un objeto de clase o tipo matriz, cada subobjeto satisface estas restricciones para los valores
(desde C++14)

Una expresión constante es una expresión constante literal, una expresión constante de referencia o una expresión constante de dirección.

Una expresión constante literal es una expresión constante principal prvalue de tipo literal no puntero (después de la conversiones requeridas por el contexto). Una expresión constante literal de tipo matriz o clase requiere que cada subobjeto se inicialice con una expresión constante.

Una expresión constante referencia es una expresión constante principal lvalue que designa un objeto con duración de almacenamiento estático o una función.

Una expresión constante de dirección es una expresión constante principal prvalue (después de las conversiones requeridas por el contexto) de tipo std::nullptr_t o tipo puntero, que apunta a un objeto con duración de almacenamiento estático, uno más allá del final de una matriz con duración de almacenamiento estático, a una función, o es un puntero nulo.

(hasta C++14)
void test() {
    static const int a = std::random_device{}();
    constexpr const int& ra = a; // OK: a es una expresión constante glvalue
    constexpr int ia = a; // Error: a no es una expresión constante prvalue 
 
    const int b = 42;
    constexpr const int& rb = b; // Error: b no es una expresión constante glvalue
    constexpr int ib = b; // OK: b es una expresión constante prvalue
}

[editar] Notas

Las implementaciones no tienen permitido declarar las funciones de biblioteca como constexpr, a no ser que el estádar diga que la función es constexpr

La copia elision es obligatoria en expresiones constantes

(desde C++14)

[editar] Informe de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 2167 C++14 las referencias no miembro locales a una evaluación estaban haciendo la evaluación no constexpr referencias no miembro permitidas
CWG 1313 C++11 se permitía comportamiento indeterminado, y todas las restas de punteros estaban prohibidas se permite la resta de punteros de la misma matriz, se prohíbe el comportamiento indeterminado
CWG 1952 C++11 se requirió un comportamiento indeterminado de la biblioteca estándar para se diagnosticado no se especifica si el comportamiento indeterminado de la biblioteca se diagnostica

[editar] Ver también