Why does C++ need the scope resolution operator?
Solution 1
Why C++ doesn't use .
where it uses ::
, is because this is how the language is defined. One plausible reason could be, to refer to the global namespace using the syntax ::a
as shown below:
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
}
}
}
I don't know how Java solves this. I don't even know if in Java there is global namespace. In C#, you refer to global name using the syntax global::a
, which means even C# has ::
operator.
but I can't think of any situation in which syntax like this would be legal anyway.
Who said syntax like a.b::c
is not legal?
Consider these classes:
struct A
{
void f() { std::cout << "A::f()" << std::endl; }
};
struct B : A
{
void f(int) { std::cout << "B::f(int)" << std::endl; }
};
Now see this (ideone):
B b;
b.f(10); //ok
b.f(); //error - as the function is hidden
b.f()
cannot be called like that, as the function is hidden, and the GCC gives this error message:
error: no matching function for call to ‘B::f()’
In order to call b.f()
(or rather A::f()
), you need scope resolution operator:
b.A::f(); //ok - explicitly selecting the hidden function using scope resolution
Solution 2
Because someone in the C++ standards committee thought that it was a good idea to allow this code to work:
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;
}
Basically, it allows a member variable and a derived class type to have the same name. I have no idea what someone was smoking when they thought that this was important. But there it is.
When the compiler sees .
, it knows that the thing to the left must be an object. When it sees ::
, it must be a typename or namespace (or nothing, indicating the global namespace). That's how it resolves this ambiguity.
Solution 3
Why does C++ have the :: operator, instead of using the . operator for this purpose?
The reason is given by Stroustrup himself:
In C with Classes, a dot was used to express membership of a class as well as expressing selection of a member of a particular object.
This had been the cause of some minor confusion and could also be used to construct ambiguous examples. To alleviate this,
::
was introduced to mean membership of class and.
was retained exclusively for membership of object
(Bjarne Stroustrup A History of C++: 1979−1991 page 21 - § 3.3.1)
Moreover it's true that
they do different things, so they might as well look different
indeed
In
N::m
neitherN
norm
are expressions with values;N
andm
are names known to the compiler and::
performs a (compile time) scope resolution rather than an expression evaluation. One could imagine allowing overloading of x::y where x is an object rather than a namespace or a class, but that would - contrary to first appearances - involve introducing new syntax (to allowexpr::expr
). It is not obvious what benefits such a complication would bring.Operator
.
(dot) could in principle be overloaded using the same technique as used for->
.
(Bjarne Stroustrup's C++ Style and Technique FAQ)
Solution 4
Unlike Java, C++ has multiple inheritance. Here is one example where scope resolution of the kind you're talking about becomes important:
#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;
}
Solution 5
I always assumed C++ dot/:: usage was a style choice, to make code easier to read. As the OP writes "they do different things, so should look different."
Coming from C++, long ago, to C#, I found using only dots confusing. I was used to seeing A::doStuff();
B.doStuff();
, and knowing the first is a regular function, in a namespace, and the second is a member function on instance B.
C++ is maybe my fifth language, after Basic, assembly, Pascal and Fortran, so I don't think it's first language syndrome, and I'm more a C# programmer now. But, IMHO, if you've used both, C++-style double-colon for namespaces reads better. I feel like Java/C# chose dots for both to (successfully) ease the front of the learning curve.
Comments
-
Karu almost 4 years
(I know what the scope resolution operator does, and how and when to use it.)
Why does C++ have the
::
operator, instead of using the.
operator for this purpose? Java doesn't have a separate operator, and works fine. Is there some difference between C++ and Java that means C++ requires a separate operator in order to be parsable?My only guess is that
::
is needed for precedence reasons, but I can't think why it needs to have higher precedence than, say,.
. The only situation I can think it would is so that something likea.b::c;
would be parsed as
a.(b::c);
, but I can't think of any situation in which syntax like this would be legal anyway.
Maybe it's just a case of "they do different things, so they might as well look different". But that doesn't explain why
::
has higher precedence than.
. -
Nicol Bolas about 12 yearsThat doesn't explain why you can't just say
b.A.f
instead ofb.A::f
. IfA
is a typename instead of a variable or function, then using.
could easily have meantscope resolution
instead of the regular meaning. -
Nicol Bolas about 12 yearsYou could still just say
.a
to mean the global one, andM.a
to mean the one in theM
namespace. -
Nawaz about 12 years@NicolBolas: The syntax
.a
looks awkward when the syntax.
is mostly used for member-of or something. -
Nicol Bolas about 12 yearsTechnically that's not about multiple inheritance. It's about being able to name your variables the same names as your derived classes.
-
Nicol Bolas about 12 yearsMaybe, but if you'd been looking at it for 10 years it wouldn't. Personally,
::
looks more awkward even after 10 years. -
Nawaz about 12 yearsWhy can't you write
test.foo.blah = 10
? Ortest.base.blah = 10
wherebase
is a keyword? -
Nicol Bolas about 12 years@Nawaz: Because introducing a keyword is a lot harder than introducing an operator. And
test.foo.blah
is ambiguous; is it the base-class'sblah
or thethingy
member'sblah
? Java (as I understand it) gets around this by stating that it's always the member; you can only get at base class member variables by casting the type. -
Mankarse about 12 years@Nawaz: Because this would not give any way of specifying which
base
you wanted to use. -
Nawaz about 12 years@NicolBolas: It maybe harder from compiler author perspective, but from programmers perspective
base.blah
is lot easier (and less awkward). -
Nicol Bolas about 12 yearsAre you saying that a parser couldn't simply wait and check the next token to see if it's a
.
before deciding on the ambiguity? -
Nicol Bolas about 12 years@Nawaz: Unless of course your code ever used the identifier
base
anywhere. Which is entirely possible. Keywording things isn't hard because of the compiler; it's hard because it makes things that used those keywords break. Even a context-specific keyword means you can't have a type namedbase
. -
Nawaz about 12 years@NicolBolas: C++ has introduced many
new
keywords (pun unintended) :new
,delete
,bool
, etc. Why notbase
? :| -
Nicol Bolas about 12 years@Nawaz: Let's also not forget that
base
isn't going to help you with multiple inheritance. -
Nawaz about 12 yearsNicolBolas: Haha. I knew that you would bring that point (which I was waiting for :D). Anyway, it means your answer isn't complete in itself, as it technically depends on another feature of C++ called multiple inheritance. But I also note that you commented on @dasblinkenlight's answer, saying : "Technically that's not about multiple inheritance. [..]".... :D
-
Karu about 12 yearsThe assumption I made about
a.b::c
not being sensible is what was causing my confusion. Accepted this answer because I think it's as good as the others but also points out my mistake. -
Karl Knechtel about 12 yearsThere is no global namespace in Java because everything is inside one class or another.
-
Nicol Bolas about 12 years@Nawaz: What? You're suggesting a keyword, which isn't a working suggestion because of multiple inheritance. You brought it up; it had nothing to do with what I or the OP said. The OP is asking why there is a difference between member field selection and scope selection. He's asking why there is special syntax to state one vs. the other;
.base
is no better in this regard than::
. He's asking why they can't be the same. And I answered that. -
nolandda about 12 yearsNo, certainly such a parser could be written. The intermediate result would be ambiguous and when the next token comes in you can assume that the user didn't mean to make a syntax error. So it isn't strictly necessary in that sense, but the '::' operator is useful elsewhere and C++ parser authors have enough problems already. :)
-
Jules about 8 yearsEarly versions of C++ changed from
.
to::
for some reason, and as namespaces were not implemented until much later than that change it is unlikely that this is the motivation. -
Jules about 8 yearsC++ did not have multiple inheritance when the
::
operator was introduced. See the Cfront E manual, page 22 (25 in the pdf) -::
in use, but no sign of multiple inheritance in the description of classes. -
Jules about 8 years@nolandda The first version of C++, "Cpre" had a working parser that used
.
for scope resolution (See source code of Cfront, which was written to be compiled by Cpre: softwarepreservation.org/projects/c_plus_plus/cfront/release_e/…). Having figured out how to do it, I doubt Stroustrup would have then backed down from doing it again when he reimplemented the language. I therefore don't think the technical difficulty of using it was part of the reason. -
Rusty Shackleford almost 7 years
I don't know how Java solves this.
FWIW, the D language uses syntax similar to Java and it solves it just by prefixing a dot to the name. dlang.org/spec/module.html#module_scope_operators This is essentially what @NicolBolas discussed earlier. -
anton_rh over 3 years
a.(A.f)()
would be ok for me. -
anton_rh over 3 years
test.(foo.blah) = 10
would be ok for me.