Quiero tener una static const
char
matriz 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, entonces 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
}
c++
c++11
static-members
constexpr
Pubby
fuente
fuente
int
@MooingDuck. Funciona bien como no miembro. ¿No violaría eso la regla también?int
s tramposo. Como no miembro, eso no debería permitirse, a menos que las reglas cambien para C ++ 11 (posible)Respuestas:
Agregar a su archivo cpp:
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 clase, pero la definición de miembro debe estar separada.
fuente
decltype(foo::baz) constexpr foo::baz;
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 casoconstexpr
implicainline
para las variables miembro estáticas. La propuesta dice:y modificado [basic.def] p2:
y agregue [depr.static_constexpr] :
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 ++ 119.4.2
[class.static.data] que dice ( énfasis mío en el futuro ):Entonces la pregunta es, ¿se
baz
usa odr aquí: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: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 quebaz
es una matriz. Esto está cubierto en la sección4.1
[conv.lval] que dice: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.
fuente
constexpr
tiene absolutamente nada que ver con eso? (baz
es una expresión constante de todos modos)integral or enumeration type
pero, de lo contrario, sí, lo que importa es que es una expresión constante .Esto es realmente una falla en C ++ 11: como han explicado otros, en C ++ 11 una variable miembro estática constexpr, a diferencia de cualquier otro tipo de variable global constexpr, tiene un enlace externo, por lo que debe definirse explícitamente en alguna parte.
También vale la pena señalar que, en la práctica, a menudo puede salirse con la suya con variables miembro estáticas constexpr sin definiciones al compilar con la optimización, ya que pueden terminar en línea en todos los usos, pero si compila sin optimización, a menudo su programa no podrá vincularse. Esto lo convierte en una trampa oculta muy común: su programa se compila bien con la optimización, pero tan pronto como apaga la optimización (quizás para la depuración), no se puede vincular.
Sin embargo, buenas noticias: ¡esta falla se solucionó en C ++ 17! Sin embargo, el enfoque es un poco complicado: en C ++ 17, las variables miembro estáticas constexpr están implícitamente en línea . La aplicación en línea a las variables es un concepto nuevo en C ++ 17, pero efectivamente significa que no necesitan una definición explícita en ninguna parte.
fuente
¿No es la solución más elegante cambiar la
char[]
:De esta manera podemos tener la definición / declaración / inicializador en 1 línea de código.
fuente
char[]
puede usarsizeof
para obtener la longitud de la cadena en tiempo de compilación, conchar *
no puede (devolverá el ancho del tipo de puntero, 1 en este caso).sizeof
problema, y puede usarse en soluciones de "solo encabezado"Mi solución para el enlace externo de los miembros estáticos es usar
constexpr
captadores de miembros de referencia (que no se encuentra con el problema que @gnzlbg planteó como un comentario a la respuesta de @deddebme).Este modismo es importante para mí porque odio tener múltiples archivos .cpp en mis proyectos e intento limitar el número a uno, que consiste en nada más que
#include
unamain()
función.fuente
En mi entorno, gcc vesion es 5.4.0. Agregar "-O2" puede corregir este error de compilación. Parece que gcc puede manejar este caso cuando solicita la optimización.
fuente