¿Se puede combinar constexpr con volátil?

5 minutos de lectura

avatar de usuario de user4637702
usuario4637702

El siguiente fragmento funciona bien en Clang 3.5 pero no en GCC 4.9.2:

int main()
{
    constexpr volatile int i = 5;
}

con error:

error: tanto ‘volátil’ como ‘constexpr’ no se pueden usar aquí

Si inspecciono el ensamblaje que genera Clang, muestra 5 como se esperaba:

movl    $5, -4(%rsp)

En el CCG, constexpr int i = 5 está optimizado, pero volatile int i = 5 también muestra 5 en la asamblea volatile const int i = 5 compila en ambos compiladores. No es un concepto extraño que algo sea volátil y constante al mismo tiempo.

¿Qué compilador es correcto según los estándares?

  • constexpr y const no son lo mismo

    – BЈовић

    5 de marzo de 2015 a las 17:13

  • @TheBuzzSaw: volatile no significa que nada tenga tendencia a cambiar. simplemente significa que el acceso a la memoria puede tener efectos secundarios y, por lo tanto, el compilador debe tratarlo como E/S. Puede muy bien ser necesario usar volatile en un sistema incluso cuando se garantiza que el valor permanecerá constante.

    – usuario541686

    5 de marzo de 2015 a las 17:14


  • @ user4637702 Sí, pero de otra manera no es cierto. Por ejemplo, intente crear un constexpr std::string objeto.

    – BЈовић

    5 de marzo de 2015 a las 17:21

  • @TheBuzzSaw tal uso de un booleano como lo describe es un comportamiento indefinido.

    – usuario541686

    5 de marzo de 2015 a las 17:45

  • @TheBuzzSaw Por ejemplo, const volatile podría tener sentido para un mapa de memoria de solo lectura de un archivo que otro proceso está modificando simultáneamente, o registros mapeados en memoria de solo lectura de un dispositivo de hardware.

    – Casey

    5 de marzo de 2015 a las 17:59


Avatar de usuario de Shafik Yaghmour
Shafik Yaghmour

Sí, esto es válido, hubo informe de defectos 1688: Variables constexpr volátiles que se archivó para esto, diciendo:

No parece haber lenguaje en la redacción actual que indique que constexpr no se puede aplicar a una variable de tipo calificado como volátil. Además, la redacción en 5.19 [expr.const] el párrafo 2 que hace referencia a “un objeto no volátil definido con constexpr” podría inducir a inferir que la combinación está permitida pero que tal variable no puede aparecer en una expresión constante. ¿Cuál es la intención?

fue rechazado como no un defecto (NAD), la respuesta y justificación fue:

La combinación se permite intencionalmente y podría usarse en algunas circunstancias para forzar la inicialización constante.

Como señala el DR, dicha variable en sí misma no se puede utilizar en un expresión constante:

constexpr volatile int i = 5;    
constexpr int y = i ;         // Not valid since i is volatile

Sección [expr.const]/2 incluye todos los casos que hacen que una expresión condicional no sea una expresión constante central, incluidos:

una conversión de lvalue a rvalue (4.1) a menos que se aplique a

y todas las excepciones requieren:

[…]que se refiere a un no volátil […] objeto […]

  • yo supo tenía que haber un DR, pero no pudo encontrar uno. Bien hecho.

    – Casey

    5 de marzo de 2015 a las 18:42

Avatar de usuario de Casey
Casey

Citando N4140 [dcl.constexpr]/9:

A constexpr especificador utilizado en una declaración de objeto declara el objeto como const. Dicho objeto tendrá un tipo literal y se inicializará.

El tipo literal se define en [basic.types]/10:

Un tipo es un tipo literal si es:

(10.1) — void; o

(10.2) — un tipo escalar; o

(10.3) — un tipo de referencia; o

(10.4) — una matriz de tipo literal; o

(10.5) — un tipo de clase (Cláusula 9) que tiene todas las siguientes propiedades:

(10.5.1) — tiene un destructor trivial,

(10.5.2) — es un tipo agregado (8.5.1) o tiene al menos una constexpr constructor o plantilla de constructor que no es un constructor de copia o movimiento, y

(10.5.3) — todos sus miembros de datos no estáticos y clases base son de tipos literales no volátiles.

El tipo escalar está en el párrafo 9:

Tipos aritméticos (3.9.1), tipos de enumeración, tipos de puntero, tipos de puntero a miembro (3.9.2), std::nullptr_ty las versiones calificadas cv de estos tipos (3.9.3) se denominan colectivamente tipos escalares.

int es aritmética, entonces volatile int es un tipo escalar y por lo tanto un tipo literal. constexpr volatile int i = 5; es por lo tanto una declaración bien formada.

Curiosamente, una expresión que evalúa i no puede ser un expresión-constante-núcleo ya que aplica una conversión de lvalue a rvalue a un glvalue de tipo volátil ([expr.const]/2). En consecuencia, las expresiones que evalúan i no son ninguno expresiones constantes integrales ni expresiones constantes. no estoy seguro de que el constexpr en esa declaración tiene algún efecto más allá de hacer i implícitamente consty (guiño a @TC) requiriendo que su inicializador sea una expresión constante.

He informado esto como GCC error 65327veremos qué tiene que decir la gente de GCC.

Actualización del 16 de marzo de 2015: se ha corregido un error para GCC 5.

  • La presencia de “no volátil” en (10.5.3) me dice que es al menos algo probable que el estándar pretendiera excluir los tipos calificados como volátiles en general, y permitir esto fue un descuido.

    usuario1084944

    5 de marzo de 2015 a las 17:57


  • “No estoy seguro de que constexpr en esa declaración tenga algún efecto más allá de hacer i implícitamente const”. – Todavía restringe el inicializador para que sea una expresión constante.

    – CT

    5 de marzo de 2015 a las 17:57

  • Por lo que vale, aquí está el parche que introdujo este error. La prueba en wandbox muestra que el error se introdujo entre 4.5.4 y 4.6.4.

    – usuario4637702

    5 de marzo de 2015 a las 18:18

  • @TC de hecho, esta es la razón que utilizó el comité en el informe de defectos que menciono en mi respuesta. Sabía que tenía que haber un informe de defectos para esto.

    – Shafik Yaghmour

    5 de marzo de 2015 a las 18:36

  • @ user4637702 Ese cambio fue para permitir const Juntos con constexpr. Revisión 166013 introdujo los diversos constexpr comprobaciones, incluido el error cuando constexpr y volatile se usan juntos.

    – Casey

    5 de marzo de 2015 a las 18:40

¿Ha sido útil esta solución?