una significado integracion indefinida ejercicios ejemplos determinacion definida define constante como calcular antiderivada c++ language-lawyer c++14 constexpr

c++ - significado - integral definida de una constante



¿Por qué necesitamos las dos definiciones: expresión constante integral y expresión constante convertida? (3)

§5.19 / 3 en C ++ 14 define una expresión constante integral y una expresión constante convertida:

Una expresión constante constante es una expresión de tipo de enumeración integral o sin ámbito, convertida implícitamente en un prvalue, donde la expresión convertida es una expresión constante central. [Nota: tales expresiones pueden usarse como límites de matriz (8.3.4, 5.3.4), como longitudes de campo de bits (9.6), como inicializadores del enumerador si el tipo subyacente no es fijo (7.2) y como alineaciones (7.6. 2). —Endente final] Una expresión constante convertida de tipo T es una expresión, convertida implícitamente en un prvalor de tipo T , donde la expresión convertida es una expresión constante de núcleo y la secuencia de conversión implícita contiene solo conversiones definidas por el usuario, valor de lvalue a valor conversiones (4.1), promociones integrales (4.5) y conversiones integrales (4.7) distintas de las conversiones de reducción (8.5.4). [Nota: tales expresiones se pueden usar en new expresiones (5.3.4), como expresiones de case (6.4.2), como inicializadores del enumerador si el tipo subyacente es fijo (7.2), como límites de matriz (8.3.4) y como Argumentos de plantilla integrales o de enumeración no tipo (14.3). "Nota final"

Tal vez me esté perdiendo algo, pero mi primera impresión es que cada expresión constante constante es una expresión constante convertida .

Editar

Y también creo que hay un error en este párrafo:

En lugar de:

A converted constant expression of type T is an expression, implicitly converted to a prvalue of type T, ...

debería ser:

A converted constant expression of type T is an expression, implicitly converted to a prvalue of an integral type, ...

Y este cambio permite compilar el siguiente código:

#include <iostream> struct A { operator int() { return 5; } } a; int main() { int b[a]{ 0, 1, 2, 3, 4 }; std::cout << b[4] << ''/n''; }

donde a en la declaración int b[a]{ 0, 1, 2, 3, 4}; es una expresión constante convertida de tipo A , convertida implícitamente a un prvalue de tipo integral ( int ) donde la expresión convertida 5 es una expresión constante central , y la secuencia de conversión implícita contiene solo una conversión definida por el usuario.


Ambas definiciones son necesarias porque hay cosas que puedes hacer con una pero no con la otra. Y no, no todas las expresiones constantes integrales son realmente una expresión constante convertida . Para el ejemplo obvio, una expresión constante convertida prohíbe las conversiones reducidas, pero una expresión constante constante no lo hace.

Por lo tanto no puedo hacer esto:

enum x : char { a = 1024 };

Sin embargo, si el inicializador para una enumeración permitiera una expresión constante constante , en lugar de una expresión constante convertida , precisamente eso sería permitido.

Como diagrama de Venn, dibujaría la situación algo como esto:

Por lo tanto, hay un poco de superposición entre los dos (probablemente más de lo que implica este diagrama) pero cada uno permite al menos algunas cosas que el otro no. He dado un ejemplo de un elemento en cada dirección, pero no he tratado de enumerar las diferencias de manera exhaustiva.

Sin embargo, no estoy completamente convencido de que las conversiones definidas por el usuario estén prohibidas para expresiones constantes integrales (y una prueba rápida muestra que los compiladores que tengo a mano les permiten en este momento). Eso daría la situación como escribí originalmente esta respuesta, que sería más como esto:


Después de la discusión de las respuestas proporcionadas por Jerry Coffin y cpplearner, permítame proponer que vuelva a escribir esas malditas reglas, como esta:

[expr.const] 5.20 / 3 (modificado)

Una expresión de constante integral es una expresión de tipo de enumeración integral o sin ámbito, que se convierte implícitamente en una expresión de constante de núcleo prvalue del mismo tipo, de modo que la secuencia de conversión implícita solo contiene una conversión de valor de lvalue a valor de r.

[expr.const] 5.20 / 4 (modificado)

Una expresión constante convertida de tipo T es una expresión de cualquier tipo, que se convierte implícitamente en una expresión constante de tipo T de tal manera que la secuencia de conversión implícita contiene solo:

  • conversiones definidas por el usuario,
  • conversiones de valores a valores,
  • conversiones de matriz a puntero,
  • conversiones de función a puntero,
  • conversiones de calificación,
  • promociones integrales,
  • conversiones integrales distintas de las conversiones reducidas,
  • conversiones de puntero nulo de std::nullptr_t ,
  • conversiones de puntero de miembro nulo de std::nullptr_t , y
  • conversiones de puntero de función,

y donde el enlace de referencia (si existe) se une directamente. [Nota: tales expresiones se pueden usar en expresiones new , como expresiones de casos, como inicializadores del enumerador si el tipo subyacente es fijo, como límites de matriz y como argumentos de plantilla no tipo. - nota final]

Ahora la diferencia es obvia, uh? También se debe recordar que de acuerdo con 5.20 / 7; 4 / 5 se puede usar una expresión de tipo de clase literal en lugar de expresión constante constante en algunos casos.


Nota: esta respuesta se basa en el último borrador de estándar por ahora, conocido como N4567. Se señalan algunas diferencias entre este y el estándar C ++ 11/14.

la expresión constante constante y la expresión constante convertida son diferentes cuando se trata de tipos de clase. En C ++ 98/03, cuando los tipos de clase no se podían usar aquí (porque no había constexpr conversión constexpr en ese momento), de hecho no existía un término como expresión constante convertida de tipo T

Para una expresión constante constante , el tipo de destino es desconocido. Pero para una expresión constante convertida de tipo T , se sabe que el tipo de destino es T , y T no es necesariamente un tipo de enumeración integral o sin ámbito 1 .

Entonces, para compilar una expresión constante constante , el compilador primero necesita decidir cuál es el tipo de destino. Si la expresión tiene un tipo de enumeración integral o sin ámbito, obviamente, el tipo de destino es solo el tipo de la expresión. De lo contrario, si la expresión tiene un tipo de clase literal (llamemos a este tipo E ), se usa el siguiente proceso 2 :

El compilador examina todas las funciones de conversión no explícitas en E 3 . Digamos que los tipos de resultados de estas funciones forman un conjunto S Si S contiene exactamente un tipo de enumeración integral o sin ámbito (el modificador de referencia se elimina y volatile calificadores const y volatile se ignoran: const volatile int& se considera como int en este proceso), entonces el tipo de destino es exactamente ese tipo. De lo contrario, la determinación falla.

(Es importante tener en cuenta que en el proceso mencionado anteriormente, las plantillas de la función de conversión no se examinan).

Como consecuencia, por ejemplo, si un tipo de clase tiene dos funciones de conversión, una es constexpr operator int y la otra constexpr operator long , entonces este tipo no se puede usar en una expresión constante constante (el tipo de destino es indecidible). Sin embargo, tal tipo se puede usar en una expresión constante convertida de tipo int o en una expresión constante convertida de tipo long .

Después de decidir el tipo de destino D , se aplica la resolución de sobrecarga para encontrar la función de conversión o la plantilla de función más adecuadas, y luego se llama a la función de conversión elegida (que debe ser constexpr ) para producir un valor de tipo D - Esta parte es, más o menos, lo mismo que una expresión constante convertida de tipo D

En el siguiente ejemplo, Var{} es una expresión constante constante válida, pero es una expresión constante convertida no válida de tipo std::size_t (el ejemplo está inspirado en esta pregunta ).

class Var { public: constexpr operator int () { return 42; } template <typename T> constexpr operator T () = delete; }; enum { x = Var{} // the initializer of `x` is expected to be an // integral constant expression // x has value 42 }; int t[ Var{} ]; // the array bound is expected to be a // converted constant expression of type std::size_t // this declaration is ill-formed

Referencias

N4567 5.20 [expr.const] p7

Si se usa una expresión de tipo de clase literal en un contexto donde se requiere una expresión constante constante, entonces esa expresión se convierte implícitamente de manera contextual (Cláusula 4) a un tipo de enumeración integral o sin ámbito y la función de conversión seleccionada será constexpr .

N4567 4 [conv] p5

Ciertas construcciones de lenguaje requieren la conversión a un valor que tenga uno de un conjunto específico de tipos apropiados para la construcción. Se dice que una expresión e del tipo de clase E aparece en dicho contexto se convierte contextualmente implícitamente en un tipo específico T y está bien formada si y solo si e puede convertirse implícitamente a un tipo T que se determina de la siguiente manera: E es buscó funciones de conversión no explícitas cuyo tipo de retorno sea cv T o referencia a cv T modo que T sea ​​permitido por el contexto. Debe haber exactamente una tal T

Notas

  1. En C ++ 11/14, una expresión constante convertida solo podría ser de tipo integral o de enumeración. N4268 cambia eso.
  2. En C ++ 11, no hubo tal proceso, en cambio, se requiere que "el tipo de clase tenga una única función de conversión no explícita a un tipo de enumeración o integral y que la función de conversión sea constexpr ". N3323 cambió eso a la redacción actual.
  3. La palabra "no explícito" no existía en el estándar C ++ 14. Fue añadido por CWG 1981 .