C ++ 17 introduce variables en línea
C ++ 17 corrige este problema para las constexpr staticvariables 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 inlineespecificador a las variables. En particular para este caso constexprimplica inlinepara 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 constexprse extendió a los tipos literales .
En C ++ 11, no necesitamos proporcionar una definición de alcance de espacio de nombres para un constexprmiembro 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 bazlo 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 bazes 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.