c++ - una - que es declaracion de variables
¿Cómo se debe hacer referencia a una Plantilla de Variable en C++ 14 cuando se declara en el alcance de Clase? (1)
Copié tu primer código en Visual Studio 2015 (se compiló correctamente). Agregué algunas salidas a través de std::cout
, donde encontré que el uso de b
arroja un error de compilación: se uninitialized local variable ''b'' used
. a
por otro lado, se imprimió con éxito cuando b
no se usó. Por lo tanto, parece que c ++ es un poco quisquilloso al acceder a los miembros estáticos de la plantilla como usted dijo, lo que requiere que haga referencia a él por su nombre completo y calificado.
Quizás más curioso, son las siguientes líneas:
std::cout << example::var<int> << "a/n";
La línea anterior funciona como se esperaba, dando como resultado 1.5
truncado a 1
y una ''a''
con una nueva línea. No hay nada que destacar.
std::cout << obj.var<int> << "b/n";
Ahora aquí es donde se pone interesante ... No solo la línea anterior no imprime un valor para obj.var<int>
, la ''b''/n
nunca se imprime tampoco. Incluso probé en contra de las funciones good()
fail()
y bad()
de std::cout
, ninguna de las cuales informaba que algo estaba mal (y se ejecutaban satisfactoriamente otros usos de std::cout
).
Otra rareza que encontré fue que auto x = obj.var
es legal, y vamos a buscar, x es de tipo example
. Ahora, hacer esto con una variable de plantilla global da como resultado un error de compilación (como también esperaba el primero):
template<typename T> constexpr T ex = 1.5;
auto x = ex // compiler error: argument list for variable template "ex" is missing
Además, descubrí que el acceso var
través de otra función estática de plantilla fue exitosa, lo que implica que la selección de miembro simplemente no funciona en este caso
class example
{
public:
template <class T> static constexpr T var = T(1.5);
template <typename T> static void thing()
{
std::cout << var<T> << ''/n''; // works
std::cout << example::var<T> << ''/n''; // also works
}
};
Ahora, en lo que respecta al estándar, me inclino a creer que su fraseo es solo un poco ... pedante. Las secciones que ha citado del estándar:
no es necesario utilizar la sintaxis de acceso de los miembros de la clase (5.2.5) para referirse a un miembro estático.
y
Una plantilla variable en el alcance de clase es una plantilla de miembro de datos estáticos.
parecería implicar que esto funcionaría. Creo que el punto donde estas citas no se aplican en este caso es la tecnicidad de que una plantilla (de cualquier cosa) realmente no "exista" hasta que se instancia en una unidad de compilación (es decir, por qué el código para plantillas a menudo se incluye en el archivo de encabezado en sí).
Debido a esto, aunque la variable de plantilla puede ser miembro de una clase, sus instancias no son ... por alguna razón ... y por lo tanto requieren el operador de resolución de alcance en comparación con el operador de selección de miembros.
Pero IMO, el acceso a datos estáticos a través del operador de selección de miembros es una mala práctica, ya que los datos y las funciones estáticas no son realmente parte de un objeto dado. Acceder a los datos estáticos de la misma manera que los datos no estáticos puede hacer que el código de aspecto relativamente inocente sea realmente defectuoso. Por ejemplo, si por alguna razón tiene un miembro estático no const llamado something
, podría escribir example_object.something = 42
, sin esperar que cambie nada para todas las demás instancias de esa clase en su programa (los mismos problemas que las variables globales, realmente ) Debido a esto (y al hecho de que la sintaxis de acceso de los miembros aparentemente no funciona para las variables de miembro estáticas de la plantilla de todos modos), recomiendo usar siempre la resolución del alcance para acceder / modificar contenido estático fuera de la clase. example_class::something = 42
es mucho más claro que estamos cambiando something
para todas las instancias de example_class
. De hecho, algunos lenguajes más modernos como C # requieren que accedas a datos estáticos a través del nombre de la clase a menos que estés dentro de dicha clase.
Dado que varios compiladores están cometiendo errores en diferentes partes de este pequeño ejemplo de programa, estoy dispuesto a apostar que no está cubierto muy bien en el estándar (y probablemente no se use muy a menudo en la práctica), y los compiladores solo lo manejan de manera diferente (otro razón para evitarlo).
tl; dr
Aparentemente, aunque la sintaxis de selección de miembros funciona para las variables de miembros estáticos, no funciona para las variables miembro estáticas de la plantilla (aunque el compilador no parece quejarse). Sin embargo, la sintaxis de resolución de alcance funciona, y la IMO debería preferirse de todos modos.
Por ejemplo:
class example{
public:
template <class T> static constexpr T var = T(1.5);
};
int main(){
int a = example::var<int>;
example obj;
int b = obj.var<int>;
return 0;
}
GCC produce un error para ambos: ''example::var<T>'' is not a function template
y ''var'' is not a member template function
Clang compila correctamente el primero pero produce un error para el segundo: cannot refer to member ''var'' in ''example'' with ''.''
Según el estándar C ++ 14 (ISO / IEC 14882: 2014):
Sección 14, Párrafo 1.
Una plantilla variable en el alcance de clase es una plantilla de miembro de datos estáticos.
Sección 9.4, Párrafo 2.
Se puede hacer referencia a un miembro estático s de clase X utilizando la expresión de id. Calificada X :: s; no es necesario utilizar la sintaxis de acceso de los miembros de la clase (5.2.5) para referirse a un miembro estático. Se puede hacer referencia a un miembro estático utilizando la sintaxis de acceso del miembro de la clase, en cuyo caso se evalúa la expresión del objeto.
Por lo tanto, en mi humilde opinión, una plantilla de variable en el alcance de la clase (es decir, una plantilla de miembro de datos estáticos) podría referirse a ambas maneras. ¿Podría ser un error en los compiladores?
Lo único que encontré para tratar de justificar este comportamiento es esta oración en la Sección 9.4.2, Párrafo 1:
Un miembro de datos estáticos no es parte de los subobjetos de una clase.
Sin embargo, los dos párrafos mencionados anteriormente siguen siendo válidos. Además, probé el mismo ejemplo haciendo referencia a otros miembros estáticos, como una variable, una función y una plantilla de función, y todos compilan con éxito tanto en GCC como en Clang.
class example{
public:
static int constexpr variable = 1;
void static function(){ return; }
template <class T> void static function_template(){ return; }
};
int main(){
example obj;
int a = obj.variable;
int b = example::variable;
obj.function();
example::function();
obj.function_template<int>();
example::function_template<int>();
return 0;
}
Gracias por adelantado.
Nota 1: las versiones del compilador son clang 3.7.0 y gcc 5.2.1.
Nota 2: se requiere la palabra clave static
: plantilla de variables en el alcance de la clase
Nota 3: dado que quiero inicializar la plantilla de variable, la palabra clave constexpr
también es necesaria porque en mi código real la constexpr
con float, double y long double (vea C ++ 14 Standard (ISO / IEC 14882: 2014), Sección 9.4.2, Párrafo 3).
Nota 4: las "definiciones" actuales de estos miembros de datos estáticos fuera de la clase (es decir, template <class T> constexpr T example::var;
) no son necesarios en este caso de ejemplo. También lo intenté, pero no importa.