C ++ 17 introduce variables en línea
C ++ 17 corrige este problema para las constexpr static
variables miembro que requieren una definición fuera de línea si se usaba odr. Consulte la segunda mitad de esta respuesta para obtener detalles anteriores a C ++ 17.
La propuesta P0386 Variables en línea introduce la capacidad de aplicar el inline
especificador a las variables. En particular para este caso constexpr
implica inline
para las variables miembro estáticas. La propuesta dice:
El especificador en línea se puede aplicar tanto a las variables como a las funciones. Una variable declarada en línea tiene la misma semántica que una función declarada en línea: se puede definir, de manera idéntica, en múltiples unidades de traducción, se debe definir en cada unidad de traducción en la que se usa odr, y el comportamiento del programa es como si Hay exactamente una variable.
y modificado [basic.def] p2:
Una declaración es una definición a menos que
...
- declara un miembro de datos estático fuera de una definición de clase y la variable se definió dentro de la clase con el especificador constexpr (este uso está en desuso; consulte [depr.static_constexpr]),
...
y agregue [depr.static_constexpr] :
Para compatibilidad con estándares internacionales anteriores de C ++, un miembro de datos estáticos constexpr puede ser redeclarado de forma redundante fuera de la clase sin inicializador. Este uso está en desuso. [Ejemplo:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
- ejemplo final]
C ++ 14 y anterior
En C ++ 03, solo se nos permitía proporcionar inicializadores en clase para integrales const o tipos de enumeración const , en C ++ 11 el uso constexpr
se extendió a los tipos literales .
En C ++ 11, no necesitamos proporcionar una definición de alcance de espacio de nombres para un constexpr
miembro estático si no se usa odr , podemos ver esto en el borrador de la sección estándar de C ++ 11 9.4.2
[class.static.data] que dice ( énfasis mío en el futuro ):
[...] Un miembro de datos estáticos de tipo literal se puede declarar en la definición de clase con el especificador constexpr; en caso afirmativo, su declaración especificará un inicializador de llave o igual en el que cada cláusula de inicializador que sea una expresión de asignación es una expresión constante. [Nota: en ambos casos, el miembro puede aparecer en expresiones constantes. —Nota final]
El miembro todavía se definirá en un ámbito de espacio de nombres si se utiliza odr-(3.2) en el programa y la definición del ámbito de nombres no contendrá un inicializador.
Entonces la pregunta es, ¿se baz
usa odr aquí:
std::string str(baz);
y la respuesta es sí , por lo que también necesitamos una definición del alcance del espacio de nombres.
Entonces, ¿cómo determinamos si una variable se usa odr ? La redacción original de C ++ 11 en la sección 3.2
[basic.def.odr] dice:
Una expresión se evalúa potencialmente a menos que sea un operando no evaluado (Cláusula 5) o una subexpresión del mismo. 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 se aplique inmediatamente la conversión lvalue-to-rvalue (4.1) .
Por baz
lo tanto , produce una expresión constante, pero la conversión lvalue-to-rvalue no se aplica inmediatamente ya que no es aplicable debido a que baz
es una matriz. Esto está cubierto en la sección 4.1
[conv.lval] que dice:
Un valor de gl (3.10) de un tipo T sin función y sin matriz se puede convertir en un valor de valor.53 [...]
Lo que se aplica en la conversión de matriz a puntero .
Esta redacción de [basic.def.odr] se modificó debido al Informe de defectos 712 ya que algunos casos no estaban cubiertos por esta redacción, pero estos cambios no cambian los resultados para este caso.