and - constexpr in c++
Referencia no definida a constexpr char estático (4)
¿No es la solución más elegante cambiar el char[]
en:
static constexpr char * baz = "quz";
De esta forma podemos tener la definición / declaración / inicializador en 1 línea de código.
Quiero tener una matriz static const
char
en mi clase. GCC se quejó y me dijo que debería usar constexpr
, aunque ahora me dice que es una referencia indefinida. Si hago que la matriz no sea miembro, se compila. Que esta pasando?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
Agregue a su archivo cpp:
constexpr char foo::baz[];
Motivo: debe proporcionar la definición del miembro estático y la declaración. La declaración y el inicializador van dentro de la definición de la clase, pero la definición del miembro debe estar separada.
En C ++ 03 solo se nos permitió proporcionar intializadores de clase para integrales const o tipos de enumeración const , en C ++ 11 usando constexpr esto se extendió a tipos literales .
En C ++ 11 no es necesario que proporcionemos una definición de ámbito de espacio de nombres para un miembro de constexto 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; de ser así, su declaración deberá especificar un inicializador de llave o igual en el que cada cláusula de inicializador que sea una expresión de asignación sea una expresión constante. [Nota: en ambos casos, el miembro puede aparecer en expresiones constantes. -finalizar nota] 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 del alcance del espacio de nombres no contendrá un inicializador.
Entonces, la pregunta es: ¿se usa baz
odr aquí?
std::string str(baz);
y la respuesta es sí , por lo que también necesitamos una definición de ámbito de 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 es potencialmente evaluada a menos que sea un operando no evaluado (cláusula 5) o una subexpresión de la misma. Una variable cuyo nombre aparece como una expresión potencialmente evaluada se utiliza 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 .
Así que baz
produce una expresión constante, pero la conversión lvalue-a-rvalue no se aplica inmediatamente ya que no es aplicable debido a que baz
es una matriz. Esto se trata en la sección 4.1
[conv.lval] que dice:
Un glvalue (3.10) de un tipo T no funcional, no de matriz se puede convertir a un prvalue.53 [...]
Qué se aplica en la conversión de matriz a punta .
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.
Esto es realmente un defecto en C ++ 11 - como otros lo han explicado, en C ++ 11 una variable constestatica estática, a diferencia de cualquier otro tipo de variable global constexpr, tiene un enlace externo, por lo tanto debe definirse explícitamente en alguna parte.
También vale la pena señalar que a menudo puede salirse con la suya con las variables de constestación estáticas sin definiciones al compilar con optimización, ya que pueden terminarse en todos los usos, pero si compila sin optimización a menudo su programa no podrá vincular. Esto hace que esto sea una trampa oculta muy común: su programa compila bien con la optimización, pero tan pronto como desactiva la optimización (quizás para la depuración), no se puede vincular.
Buenas noticias, ¡esta falla se corrigió en C ++ 17! Sin embargo, el enfoque es un poco intrincado: en C ++ 17, las variables estáticas de constexto están implícitamente en línea . Tener aplicaciones en línea aplicadas a variables es un concepto nuevo en C ++ 17, pero significa que no necesitan una definición explícita en ningún lugar.