Me gustaría tener una constante estática privada para una clase (en este caso, una fábrica de formas).
Me gustaría tener algo por el estilo.
class A {
private:
static const string RECTANGLE = "rectangle";
}
Desafortunadamente, recibo todo tipo de errores del compilador de C ++ (g ++), como:
ISO C ++ prohíbe la inicialización del miembro 'RECTANGLE'
Inicialización en clase no válida del miembro de datos estáticos de tipo no integral 'std :: string'
error: haciendo estático 'RECTANGLE'
Esto me dice que este tipo de diseño de miembro no cumple con el estándar. ¿Cómo se tiene una constante literal privada (o quizás pública) sin tener que usar una directiva #define (¡quiero evitar la fealdad de la globalidad de los datos!)
Cualquier ayuda es apreciada.
Respuestas:
Debe definir su miembro estático fuera de la definición de clase y proporcionar el inicializador allí.
primero
y entonces
La sintaxis que originalmente intentaba usar (inicializador dentro de la definición de clase) solo se permite con tipos integrales y enum.
A partir de C ++ 17, tiene otra opción, que es bastante similar a su declaración original: variables en línea
No se necesita una definición adicional.
O en lugar de
const
usted puede declararloconstexpr
en esta variante. Explícitoinline
ya no sería necesario, ya queconstexpr
implicainline
.fuente
char const*
tiene la bondad que se inicia antes de que termine toda la inicialización dinámica. Entonces, en el constructor de cualquier objeto, puede confiar enRECTANGLE
que ya se haya inicializado.En C ++ 11 puedes hacer ahora:
fuente
constexpr
implicaconst
para var, no para escribir puntos. Es decir,static constexpr const char* const
es lo mismo questatic constexpr const char*
, pero no es lo mismo questatic constexpr char*
.Dentro de las definiciones de clase solo puede declarar miembros estáticos. Tienen que ser definidos fuera de la clase. Para las constantes integrales en tiempo de compilación, el estándar hace la excepción de que puede "inicializar" miembros. Sin embargo, todavía no es una definición. Tomar la dirección no funcionaría sin definición, por ejemplo.
Me gustaría mencionar que no veo el beneficio de usar std :: string sobre const char [] para constantes . std :: string es agradable y todo pero requiere una inicialización dinámica. Entonces, si escribes algo como
en el ámbito del espacio de nombres, el constructor de foo se ejecutará justo antes de la ejecución de los inicios principales y este constructor creará una copia de la constante "hola" en la memoria del montón. A menos que realmente necesite RECTANGLE para ser un std :: string, también podría escribir
¡Allí! Sin asignación de montón, sin copia, sin inicialización dinámica.
Saludos, s.
fuente
Esto es solo información adicional, pero si realmente desea la cadena en un archivo de encabezado, intente algo como:
Aunque dudo que sea recomendable.
fuente
En C ++ 17 puede usar variables en línea :
Tenga en cuenta que esto es diferente de la respuesta de abyss.7 : este define un
std::string
objeto real , no unconst char*
fuente
inline
creará muchos duplicados?Esta es la restricción. Por lo tanto, en este caso necesita definir variables fuera de la clase. refiera la respuesta de @AndreyT
fuente
Las variables estáticas de clase se pueden declarar en el encabezado pero se deben definir en un archivo .cpp. Esto se debe a que solo puede haber una instancia de una variable estática y el compilador no puede decidir en qué archivo de objeto generado colocarlo, por lo que debe tomar la decisión.
Para mantener la definición de un valor estático con la declaración en C ++ 11, se puede utilizar una estructura estática anidada. En este caso, el miembro estático es una estructura y debe definirse en un archivo .cpp, pero los valores están en el encabezado.
En lugar de inicializar miembros individuales, toda la estructura estática se inicializa en .cpp:
Se accede a los valores con
o - dado que los miembros son privados y están destinados a ser utilizados solo desde A - con
Tenga en cuenta que esta solución aún adolece del problema del orden de inicialización de las variables estáticas. Cuando se usa un valor estático para inicializar otra variable estática, la primera puede no inicializarse todavía.
En este caso, los encabezados de las variables estáticas contendrán {""} o {".h", ".hpp"}, según el orden de inicialización creado por el vinculador.
Como mencionó @ abyss.7, también podría usar
constexpr
si el valor de la variable se puede calcular en tiempo de compilación. Pero si declara sus cadenas constatic constexpr const char*
y su programa usa lostd::string
contrario, habrá una sobrecarga porque sestd::string
creará un nuevo objeto cada vez que use una constante de este tipo:fuente
El estándar actual solo permite dicha inicialización para los tipos integrales constantes estáticos. Entonces debes hacer lo que AndreyT explicó. Sin embargo, eso estará disponible en el próximo estándar a través de la sintaxis de inicialización del nuevo miembro .
fuente
posible solo haz:
o
fuente
constexpr
pero no puede hacer una función estáticaconst
.static const std::string RECTANGLE() const { static const std::string value("rectangle"); return value; }
Puede optar por la
const char*
solución mencionada anteriormente, pero si necesita una secuencia todo el tiempo, tendrá una sobrecarga.Por otro lado, la cadena estática necesita una inicialización dinámica, por lo tanto, si desea utilizar su valor durante la inicialización de otra variable global / estática, puede encontrar el problema del orden de inicialización. Para evitar eso, lo más barato es acceder al objeto de cadena estática a través de un getter, que verifica si su objeto está inicializado o no.
Recuerde usar solo
A::getS()
. Debido a que cualquier subproceso solo puede iniciarsemain()
yA_s_initialized
se inicializa antesmain()
, no necesita bloqueos incluso en un entorno multiproceso.A_s_initialized
es 0 de forma predeterminada (antes de la inicialización dinámica), por lo que si usagetS()
antes de que se inicialice s, llamará a la función init de forma segura.Por cierto, en la respuesta anterior: " static const std :: string RECTANGLE () const ", las funciones estáticas no pueden ser
const
porque no pueden cambiar el estado si algún objeto de todos modos (no hay este puntero).fuente
Avance rápido a 2018 y C ++ 17.
static_assert 'funciona' solo en tiempo de compilación
};
Arriba está un ciudadano C ++ estándar apropiado y legal. Puede involucrarse fácilmente en cualquiera y todos los algoritmos std ::, contenedores, utilidades y demás. Por ejemplo:
Disfruta del estándar C ++
fuente
std::string_view
para constantes solo si usastring_view
parámetros en todas sus funciones. Si alguna de sus funciones usa unconst std::string&
parámetro, se creará una copia de una cadena cuando pase unastring_view
constante a través de ese parámetro. Si sus constantes son de tipo,std::string
las copias no se crearán ni paraconst std::string&
parámetros ni parastd::string_view
parámetros.inline
variables llegaran a C ++ 17 con su semántica ODR. Pero string_view también es C ++ 17, por lo que soloconstexpr auto some_str = "compile time"sv;
hace el trabajo (y en realidad, no es una variable, esconstexpr
, porinline
lo tanto, está implícito; si tiene una variable, es decir, noconstexpr
),inline auto some_str = "compile time"sv;
lo hará, aunque, por supuesto, un ámbito de espacio de nombres variable, que es esencialmente una variable global, rara vez sería una buena idea).