and c++ c++11 static-members constexpr one-definition-rule

and - constexpr in c++



¿Qué puedo hacer con un miembro de datos inicializado estático, constexpr, en clase? (2)

¿Significa que una variable de constexto estática no se utiliza (y, por lo tanto, puede inicializarse en clase) si se devuelve desde una función?

Sí.

Esencialmente, siempre que lo trate como un valor , en lugar de como un objeto , entonces no se usa. Considere que si pega el valor, el código funcionaría de manera idéntica, esto es cuando se trata como un valor r. Pero hay algunos escenarios donde no sería así.

Hay solo unos pocos escenarios en los que la conversión lvalue-a-rvalue no se realiza en primitivas, y eso es vinculante de referencia, &obj y probablemente un par más, pero son muy pocos. Recuerde que, si el compilador le proporciona una const int& referencia al period , entonces debe poder tomar su dirección, y además, esta dirección debe ser la misma para cada TU . Eso significa, en el horrendo sistema TU de C ++, que debe haber una definición explícita.

Si no se utiliza, el compilador puede hacer una copia en cada TU, o sustituir el valor, o lo que quiera, y no se puede observar la diferencia.

Esta es probablemente una pregunta poco habitual, en la medida en que solicita una explicación más completa de una respuesta corta dada a otra pregunta y de algunos aspectos del Estándar C ++ 11 relacionado con ella.

Para facilitar la referencia, resumiré aquí la pregunta referenciada. El OP define una clase:

struct Account { static constexpr int period = 30; void foo(const int &) { } void bar() { foo(period); } //no error? };

y se pregunta por qué no obtiene ningún error acerca de su uso de un miembro de datos estáticos inicializados en su clase (un libro menciona que esto es ilegal). La respuesta de Johannes Schaub dice que:

  1. Esto viola la regla de una sola definición ;
  2. No se requiere diagnóstico.

Por mucho que confíe en la fuente y la validez de esta respuesta, sinceramente me desagrada porque personalmente la encuentro demasiado críptica, así que traté de encontrar una respuesta más significativa por mí mismo, con solo un éxito parcial. Relevante parece ser § 9.4.2 / 4:

"Habrá exactamente una definición de miembro de datos estáticos que se usa odr (3.2) en un programa, no se requiere diagnóstico " [Los énfasis son míos]

Lo cual me acerca un poco más al punto. Y así es como el § 3.2 / 2 define una variable odr-used :

"Una variable cuyo nombre aparece como una expresión potencialmente evaluada se usa odr a menos que sea un objeto que satisfaga los requisitos para aparecer en una expresión constante (5.19) y la conversión lvalue-a-rvalue (4.1) se aplique inmediatamente" [ Los énfasis son míos]

En la pregunta del OP, el period variable satisface claramente los requisitos para aparecer en una expresión constante, siendo una variable constexpr . Entonces la razón debe encontrarse ciertamente en la segunda condición: " y la conversión lvalue-to-rvalue (4.1) se aplica inmediatamente " .

Aquí es donde tengo problemas para interpretar el Estándar. ¿Qué significa realmente esta segunda condición? ¿Cuáles son las situaciones que cubre? ¿Significa que una variable de constexpr estática no se utiliza (y, por lo tanto, puede inicializarse en clase) si se devuelve desde una función?

De manera más general: ¿qué se le permite hacer con una variable de constexpr estática para que pueda inicializarla en clase?


Te perdiste una parte de la premisa. La definición de clase anterior es completamente válida, si también define Account::period algún lugar (pero sin proporcionar un inicializador). Vea la última sesión de 9.4.2 / 3:

El miembro se seguirá definiendo en un ámbito de espacio de nombres si se utiliza odr (3.2) en el programa y la definición de ámbito de espacio de nombres no contendrá un inicializador.

Toda esta discusión solo se aplica a los miembros de datos constexpr estáticos, no a las variables estáticas del ámbito de espacio de nombres. Cuando los miembros de datos estáticos son constexpr (en lugar de simplemente const) debe inicializarlos en la definición de la clase. Entonces su pregunta debería ser: ¿Qué se le permite hacer con un miembro de datos estáticos constexpr para que no tenga que proporcionar una definición para ello en el ámbito del espacio de nombres?

De lo anterior, la respuesta es que el miembro no debe ser usado . Y ya encontraste partes relevantes de la definición de odr-used . De modo que puede usar el miembro en un contexto en el que no se evalúa potencialmente , como un operando no evaluado (por ejemplo, de sizeof o decltype ) o una subexpresión del mismo. Y puede usarlo en una expresión donde la conversión lvalue-to-rvalue se aplica inmediatamente.

Entonces ahora estamos abajo ¿Qué usos de una variable causa una conversión inmediata de valor l a valor razonable?

Parte de esa respuesta está en §5 / 8:

Cuando una expresión glvalue aparece como un operando de un operador que espera un valor pr para ese operando, las conversiones estándar lvalue-to-rvalue (4.1), array-to-puntero (4.2) o función-puntero (4.3) son aplicado para convertir la expresión en un valor pr.

Para los tipos aritméticos que se aplican esencialmente a todos los operadores que aplican conversiones aritméticas estándar. Entonces puede usar el miembro en varias operaciones aritméticas y lógicas sin necesidad de una definición.

No puedo enumerar todas las cosas, puede o no puede hacer aquí, porque los requisitos de que algo sea un [g] lvalue o [p] rvalue están distribuidos en el estándar. La regla de oro es: si solo se usa el valor de la variable, se aplica una conversión de valor a valor r; si la variable se usa como un objeto , se usa como lvalue .

No puede usarlo en contextos en los que se requiere explícitamente un lvalue, por ejemplo, como argumento para el operador de dirección o operadores de mutación). La vinculación directa de referencias de lvalue (sin conversión) es un contexto de este tipo.

Algunos ejemplos más (sin análisis estandarizado detallado):

Puede pasarlo a funciones, a menos que el parámetro de función sea una referencia a la que la variable pueda vincularse directamente.

Para devolverlo desde una función: si la conversión implícita al tipo de devolución implica una función de conversión definida por el usuario, se aplican las reglas para pasar a una función. De lo contrario, puede devolverlo, a menos que la función devuelva un valor l (una referencia) que se refiera directamente a la variable.

La regla clave para las variables usadas , la "Regla de una definición" está en 3.2 / 3:

Cada programa debe contener exactamente una definición de cada función o variable no en línea que se utiliza en ese programa; no se requiere diagnóstico.

La parte "no requiere diagnóstico" significa que los programas que violan esta regla causan un comportamiento indefinido, que puede ir desde la falla al compilar, compilar y fallar de maneras sorprendentes para compilar y actuar como si todo estuviera bien. Y su compilador no necesita advertirle sobre el problema.

La razón, como ya han indicado otros, es que muchas de estas violaciones solo serían detectadas por un enlazador. Pero las optimizaciones pueden haber eliminado las referencias a los objetos, de modo que no existe una causa para la falla del enlace o, a veces, los enlaces solo pueden ocurrir en tiempo de ejecución o pueden ser definidos para elegir una instancia arbitraria de múltiples definiciones de un nombre.