usar - ¿Por qué C++ necesita el operador de resolución de alcance?
no me funcionan los hashtags en instagram 2018 (7)
¿Por qué C ++ tiene el operador ::, en lugar de usar el. operador para este fin?
La razón la da el propio Stroustrup:
En C con Clases, se usó un punto para expresar la pertenencia a una clase y para expresar la selección de un miembro de un objeto en particular.
Esto ha sido la causa de algunas pequeñas confusiones y también podría usarse para construir ejemplos ambiguos. Para aliviar esto,
::
se introdujo para significar membresía de clase y.
fue retenido exclusivamente para ser miembro de un objeto
(Bjarne Stroustrup Una historia de C ++: 1979-1991 página 21 - § 3.3.1)
Además, es cierto que
hacen cosas diferentes, por lo que también podrían verse diferentes
en efecto
En
N::m
niN
nim
son expresiones con valores;N
ym
son nombres conocidos por el compilador y::
realiza una resolución de ámbito (tiempo de compilación) en lugar de una evaluación de expresión. Uno podría imaginar permitir la sobrecarga de x :: y donde x es un objeto en lugar de un espacio de nombres o una clase, pero eso, a diferencia de las primeras apariencias, implica la introducción de una nueva sintaxis (para permitirexpr::expr
). No es obvio qué beneficios traería tal complicación.Operador (punto) podría, en principio, sobrecargarse utilizando la misma técnica que para
->
.
( Preguntas frecuentes sobre el estilo y la técnica C ++ de Bjarne Stroustrup)
(Sé lo que hace el operador de resolución de alcance, y cómo y cuándo usarlo).
¿Por qué C ++ tiene el operador ::
, en lugar de usar el .
operador para este fin? Java no tiene un operador separado y funciona bien. ¿Hay alguna diferencia entre C ++ y Java que signifique que C ++ requiere un operador por separado para poder analizar?
Mi única conjetura es que ::
es necesaria por razones de precedencia, pero no puedo pensar por qué debe tener una mayor prioridad que, por ejemplo,. . La única situación en la que puedo pensar es que algo así
a.b::c;
sería analizado como
a.(b::c);
, pero no puedo pensar en ninguna situación en la que la sintaxis como esta sea legal de todos modos.
Tal vez sea solo un caso de "hacen cosas diferentes, por lo que también podrían verse diferentes". Pero eso no explica por qué ::
tiene mayor precedencia que .
.
A diferencia de Java, C ++ tiene herencia múltiple. Aquí hay un ejemplo donde la resolución del alcance del tipo de lo que estás hablando se vuelve importante:
#include <iostream>
using namespace std;
struct a
{
int x;
};
struct b
{
int x;
};
struct c : public a, public b
{
::a a;
::b b;
};
int main() {
c v;
v.a::x = 5;
v.a.x = 55;
v.b::x = 6;
v.b.x = 66;
cout << v.a::x << " " << v.b::x << endl;
cout << v.a.x << " " << v.b.x << endl;
return 0;
}
El operador de resolución de alcance (: :) se usa para definir una función fuera de una clase o cuando queremos usar una variable global pero también tiene una variable local con el mismo nombre.
Por qué C ++ no usa donde usa ::
, es porque así es como se define el idioma. Una razón plausible podría ser, referirse al espacio de nombre global utilizando la sintaxis ::a
como se muestra a continuación:
int a = 10;
namespace M
{
int a = 20;
namespace N
{
int a = 30;
void f()
{
int x = a; //a refers to the name inside N, same as M::N::a
int y = M::a; //M::a refers to the name inside M
int z = ::a; //::a refers to the name in the global namespace
std::cout<< x <<","<< y <<","<< z <<std::endl; //30,20,10
}
}
}
No sé cómo Java resuelve esto. Ni siquiera sé si en Java hay espacio de nombres global. En C #, se refiere al nombre global usando la sintaxis global::a
, lo que significa que incluso C # tiene ::
operador.
pero no puedo pensar en ninguna situación en la que la sintaxis como esta sea legal de todos modos.
¿Quién dijo que la sintaxis como ab::c
no es legal?
Considera estas clases:
struct A
{
void f() { std::cout << "A::f()" << std::endl; }
};
struct B : A
{
void f(int) { std::cout << "B::f(int)" << std::endl; }
};
Ahora mira esto ( ideone ):
B b;
b.f(10); //ok
b.f(); //error - as the function is hidden
bf()
no se puede llamar así, ya que la función está oculta, y el GCC muestra este mensaje de error:
error: no matching function for call to ‘B::f()’
Para llamar a bf()
(o más bien A::f()
), necesita un operador de resolución de alcance:
b.A::f(); //ok - explicitly selecting the hidden function using scope resolution
Porque alguien en el comité de estándares de C ++ pensó que era una buena idea permitir que este código funcionara:
struct foo
{
int blah;
};
struct thingy
{
int data;
};
struct bar : public foo
{
thingy foo;
};
int main()
{
bar test;
test.foo.data = 5;
test.foo::blah = 10;
return 0;
}
Básicamente, permite que una variable miembro y un tipo de clase derivado tengan el mismo nombre. No tengo idea de lo que alguien estaba fumando cuando pensaban que esto era importante. Pero ahí está.
Cuando el compilador ve .
, sabe que la cosa a la izquierda debe ser un objeto. Cuando ve ::
, debe ser un nombre de tipo o espacio de nombre (o nada, que indica el espacio de nombre global). Así es como resuelve esta ambigüedad.
Siempre asumí que C ++ dot / :: usage era una opción de estilo, para hacer que el código sea más fácil de leer. Como escribe el OP "hacen cosas diferentes, por lo que deberían verse diferentes".
Viniendo de C ++, hace mucho tiempo, a C #, me pareció que usar solo puntos confusos. Estaba acostumbrado a ver A::doStuff();
B.doStuff();
, y saber que el primero es una función regular, en un espacio de nombres, y el segundo es una función miembro en la instancia B.
C ++ es quizás mi quinto idioma, después de Basic, assembly, Pascal y Fortran, así que no creo que sea el síndrome del primer idioma, y ahora soy más un programador de C #. Pero, en mi humilde opinión, si ha utilizado ambos, el doble punto estilo C ++ para espacios de nombres tiene una mejor lectura. Siento que Java / C # eligió puntos para que ambos (con éxito) faciliten el frente de la curva de aprendizaje.
Solo para responder al último bit de la pregunta sobre la precedencia del operador:
class A {
public:
char A;
};
class B : public A {
public:
double A;
};
int main(int c, char** v)
{
B myB;
myB.A = 7.89;
myB.A::A = ''a'';
// On the line above a hypothetical myB.A.A
// syntax would parse as (myB.A).A and since
// (myB.A) is of type double you get (double).A in the
// next step. Of course the ''.'' operator has no
// meaning for doubles so it causes a syntax error.
// For this reason a different operator that binds
// more strongly than ''.'' is needed.
return 0;
}