Thread: Scope Resolution Operator Help?

  1. #1
    Registered User
    Join Date
    Nov 2011
    Posts
    35

    Scope Resolution Operator Help?

    I have done alot of googling for the scope resolution operator and Ive gained a bit of an understanding as to what it does i know it can distinguish between global and local variables, but I see it used to access methods/members of classes such as this example, why not just use a dot instead to access it?:

    sql:: Driver *driver;

    Why is the scope resolution operator being used here? Can someone explain, thanks
    Last edited by shivam1992; 02-22-2013 at 12:02 AM.

  2. #2
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    The scope resolution operator is used to access static members of a class. The dot is used to access (usually non-static) members of an object(instance) of a class.
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  3. #3
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    Given the code

    sql:: Driver *driver;

    This is a declaration, which means sql is either a namespace, or a class, that holds the Driver class. The scope resolution operator is used because it must be used in either of these cases when a using directive is not present.

  4. #4
    Registered User
    Join Date
    Nov 2011
    Posts
    35
    so its used to access static members from a class? or when using isnt specified? , why would it be used if the using directive isnt present?

  5. #5
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    Quote Originally Posted by shivam1992 View Post
    so its used to access static members from a class? or when using isnt specified? , why would it be used if the using directive isnt present?
    Because the compiler must know which Driver is the right Driver. Wherever the scope resolution operator might be appropriate, you are referring to a particular object, or a particular method, in a particular scope. Such usage facilitates unambiguous lookup for static type checking.

    Code:
    namespace sql {
       class Driver;
    }
    
    namespace jck {
       class Driver;
    }
    
    class Driver;
    All of these Drivers are different and exist in different scopes. The global one is resolved (optionally in most cases) with ::Driver. A namespace is just a named scope, so there is also jck::Driver, and sql::Driver. In addition to the above, classes or other user-defined data types are also scopes.

    And I think I should mention that the scope resolution operator is appropriate for header files. In a header file, the using directive will also apply to where the header is included, which is troublesome because it pollutes the source file with a bunch of names, and increases the chance of a name ambiguity.
    Last edited by whiteflags; 02-22-2013 at 02:25 AM.

  6. #6
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by shivam1992 View Post
    so its used to access static members from a class?
    The scope resolution operator is about more than accessing static members of a class.

    A namespace is a (named) scope. All names within the namespace belong in a scope with the same name as the namespace. Hence, to access something named x within a namespace named n, the syntax is n::x. Things that can be named include types, variables, and other scopes (so a namespace can be nested in a namespace,

    The name of a struct or class type in C++ is also a scope, with the same name as the struct type. Any member of a struct or class type can therefore be accessed using the syntax struct_name::member_name in any setting where access of that member makes sense.

    So, it is possible to use the scope resolution to access a static member of a class/struct
    Code:
    struct X
    {
        public:
           static int foo;
    };
    
    int X::foo;    // definition needed once and only once within a program
    
    int main()
    {
           X::foo = 42;    // changes the static member of X named foo.
    }
    Naturally, the assignment "X::foo = 42" above will not compile if foo is a private or protected member of X.

    However, scope resolution can also be used to access non-static members of a struct or class. For example;
    Code:
    #include <iostream>
    
    class Y
    {
        public:
    
              Y() {};
    
              virtual int Hello();
    };
    
    int Y::Hello()    // note use of scope operator to implement Y's Hello()
    {
        return 42;
    }
    
    class Z : public Y
    {
        public:
            Z() : Y() {};
    
            int Hello();
    
    };
    
    int Z::Hello()     //   Note use of scope operator in definition of Z's Hello()
    {
        return 2*Y::Hello();      //  Note use of scope operator to call Y's Hello()
    }
    
    int main()
    {
         Y *y = new Y;
         Y *z = new Z;
    
         std::cout << y->Hello() << '\n';
         std::cout << z->Hello() << '\n';
         std::cout << y->Y::Hello() << '\n';
         std::cout << z->Y::Hello() << '\n';     // note that this does not call Z::Hello()
    }
    I mention the last line for completeness - it is rarely a good idea in practice: if a caller needs to force the compiler to call Y::Hello() rather than Z::Hello() for an object of type Z, then that is usually a sign of a class design problem.

    There are limits to scope resolution. In the last code sample, for example, the line
    Code:
        std::cout << y->Z::Hello() << '\n';
    would not compile, since y is of type Y, and it does not make sense to call Z::Hello().


    Quote Originally Posted by shivam1992 View Post
    or when using isnt specified? , why would it be used if the using directive isnt present?
    This question shows you have got the idea of scope resolution completely backward. The scope resolution operator is not something that is used to avoid the "using" directive. The "using" directive is a flawed shortcut to reduce need to employ the scope resolution operator.

    Every name is within some scope. And the complete name of a variable, x, within a scope s is s::x. Given the syntax s::x, the compiler has only two choices: it accesses the name x within scope s, or it reports an error (eg because scope s does not exist, or because scope s does not contain anything named x). There is such a thing as an unnamed namespace, so
    Code:
    int x;
    
    int main()
    {
         x = 42;
    
         ::x = 50;    //  this affects the x above.   The only difference is that we tell the compiler specifically to change the value of x in the global (unnamed) namespace
    }
    What the using directive does is tell the compiler how to search for candidate matches to unadorned names (such as x). For example;
    Code:
    namespace Junk
    {
        int x;
    }
    
    using namespace Junk;
    
    int main()
    {
        x = 42;
    }
    The using directive tells the compiler that, if it sees a name, to look for a match in namespace Junk for a candidate match. There is no x in the global namespace (i.e. there is no ::x) so the assignment "x = 42" affects the variable Junk::x.

    If the using namespace directive is removed, this code will not compile because the compiler is no longer being told to look within namespace Junk. The compiler does not match "x" to "Junk::x", so the code will not compile (as the compiler cannot find anything to match ::x)

    Whether the using namespace directive is employed or not, however, this code will always be unambiguous.
    Code:
    namespace Junk
    {
        int x;
    }
    
    using namespace Junk;    // this line is optional
    
    int main()
    {
        Junk::x = 42;
    }
    Now, the next example, we come to the point of namespaces - three things named x, each with different meaning to the program, and all in different namespaces.
    Code:
    namespace Alpha
    {
        int x;
    }
    
    namespace Beta
    {
        int x;
    }
    using namespace Alpha;
    using namespace Beta;
    
    int x;    // within unnamed namespace
    
    int main()
    {
        x = 42;
    }
    This code will simply not compile. The reason is that the compiler has been told, when it is trying to resolve a name, to look in namespaces Alpha and Beta for candidate matches. Because of that, it finds Alpha::x, Beta::x, and ::x as candidates. The compiler has no reason to prefer one candidate over another, so will reject the code (typically with an error message about some form of ambiguity).

    If we absolutely must have namespaces Alpha and Beta visible to the compiler then, when compiling this code, there are few options. The first is to remove the "using namespace" directives. So, when it sees "x", the compiler will only consider ::x as a candidate.

    If we insist on employing the "using" directives, however, there is no alternative to using the scope resolution operator to resolve the ambiguity.
    Code:
    namespace Alpha
    {
        int x;
    }
    
    namespace Beta
    {
        int x;
    }
    using namespace Alpha;
    using namespace Beta;
    
    int x;    // within unnamed namespace
    
    int main()
    {
        ::x = 42;
    
         Alpha::x = 50;
         Beta::x = 60;
    
         x = 70;     // this line will still not compile unless the "using" directives are removed
    }
    This last sample demonstrates the problem of the "using" directive - once activated, its effects cannot be turned off. So, if ambiguity occurs due to a candidate matches for a name being in multiple namespaces, the ONLY solutions are to remove the offending "using" directives, or to use the scope resolution operator. This is why "using namespace" directives in header files is strongly discouraged - it imposes problems on the code which uses the header file.
    Last edited by grumpy; 02-22-2013 at 04:42 AM.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  7. #7
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    For completeness, I should point out, using directives can be necessary sometimes. For example:
    Code:
    #include <utility>
    #include <iostream> 
    
    namespace example{
      class Foo {};
      void swap(Foo &obj1, Foo &obj2){
         std::cout << "example::swap" << std::endl;
      }
    }
    
    class Bar {};
    
    void swap(Bar &obj1, Bar &obj2){
      std::cout << "::swap" << std::endl;
    }
    
    class Baz {};
    
    template <typename T>
    void func(T &obj1, T & obj2){
       using namespace std;
       swap(obj1, obj2);
    }
    
    int main(){
      example::Foo foo1, foo2;
      Bar bar1, bar2;
      Baz baz1, baz2;
      func(foo1, foo2);//calls example::swap
      func(bar1, bar2);//calls ::swap
      func(baz1, baz2);//calls std::swap
      return 0;
    }
    Last edited by King Mir; 02-22-2013 at 01:28 PM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  8. #8
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Necessary is a strong word, King Mir. Convenient may be a better word in this case, as the same effect can be achieved by template specialisation.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  9. #9
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by grumpy View Post
    Necessary is a strong word, King Mir. Convenient may be a better word in this case, as the same effect can be achieved by template specialisation.
    Not if you don't know they types that the template would be called with. Which is part of the point of using a template. The other reason for using templates would be code reuse, which is also be undermined by writing a special case for a particular object.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  10. #10
    Registered User
    Join Date
    Nov 2011
    Posts
    35
    Wow thanks to all of you I really now get the scope resolution operator and what it does fully, thanks guys for the detailed replies, Thats why I love this forum, Lots of friendly professionals that can quickly help anyone out, thanks guys again for the detailed posts Appreciate it alot!

  11. #11
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by King Mir View Post
    Not if you don't know they types that the template would be called with. Which is part of the point of using a template. The other reason for using templates would be code reuse, which is also be undermined by writing a special case for a particular object.
    Your example also involves two special cases - the example::swap() function, and the overloaded swap(Bar &, Bar &). As such, your example inherently undermines code reuse in a manner equivalent to what occurs with a template specialisation.

    So your argument is playing both ends against the middle.
    Last edited by grumpy; 02-22-2013 at 05:46 PM.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  12. #12
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Yes, Foo and Bar have different swap implementations, but they don't know about func. Nor does func know about them. Yet func is able to use the potentially optimized swap that Foo and Bar have. You can't achieve this with template specialization.

    Here's a more specific use-case.
    Code:
    template <typename T>
    class Wrapper {
      T internals;
      friend swap(Wrapper<T> &op1, Wrapper<T> &op2){
        using namespace std;
        swap(op1.internals,op2.internals);
      }
    };
    If I put Foo or Bar, from my earlier example, in this wrapper, I would want to call the implementation of swap special to the type, but for Baz, I want to call std::swap.


    Incidentally, for the earlier example, Bar does not have to be in the global namespace, as long as the Bar version of swap is. I should have put it in namespace example to make the point.
    Last edited by King Mir; 02-22-2013 at 08:20 PM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Scope resolution in C++
    By csonx_p in forum C++ Programming
    Replies: 6
    Last Post: 01-22-2009, 12:57 PM
  2. How to avoid typing std:: (scope resolution operator)
    By Sharan in forum C++ Programming
    Replies: 9
    Last Post: 04-30-2006, 08:25 PM
  3. quick question about scope resolution
    By *ClownPimp* in forum C++ Programming
    Replies: 8
    Last Post: 11-03-2002, 10:04 PM
  4. scope resolution operator ::: ??
    By Carlos in forum Windows Programming
    Replies: 7
    Last Post: 10-01-2002, 08:56 PM
  5. Replies: 4
    Last Post: 06-04-2002, 06:09 AM