Lesson 4 | The scope resolution operator and binary form: |
Objective | Binary form of the scope resolution operator. |
Binary Scope Resolution in C++
The scope resolution operator's binary form is used to
clarify names that are reused within classes.
Redundant Scope Resolution Operator
The use of the binary form of the scope resolution operator is not usually necessary within the class definition itself or when used with memberselectors to call member functions. Take a look at this continuation of the lesson example:
widgets w;
gizmos g;
g.f();
w.f();
g.gizmos::f(); //legal and redundant
g.widgets::f(); //illegal widgets::f() cannot
//act on a gizmo
Question: Why are redundant qualifications permitted on the definition?
Answer: Yes, it's allowed.
The class-name is also inserted into the scope of the class itself;
this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name.
class widgets { public: void f(); };
class gizmos { public: void f(); };
void f() { ..... } //ordinary external f
void widgets::f() { ..... } //f scoped to widgets
void gizmos::f() { ..... } //f scoped to gizmos
Accessing Namespace Members Directly
Simply use the binary scope resolution operator with the namespace name on the left and the member on the right.
namespace A {
int x = 1;
structADT {
enumColor { RED, WHITE, BLUE };
};
}
namespace B {
int x = 2;
structADT {
enumColor { GREEN, YELLOW };
};
}
Namespaces
Namespaces address the problem of naming conflicts between different pieces of code. For example, you might be writing some code that has a function called method1(). One day, you decide to start using a third-party library, which also has a method1() function. The compiler has no way of knowing which version of foo() you are referring to within your code. You cannot change the library's function name, and it would be a big pain to change your own.
Namespaces come to the rescue in such scenarios because you can define the context in which names are defined. To place code in a namespace, enclose it within a namespace block. Suppose the following is in a file called namespaces.h:
namespace codesample {
void method1();
}
The implementation of a method or function can also be handled in a namespace:
#include <iostream>
#include "namespaces.h"
namespace codesample {
void method1() {
std::cout << "method1() called in the codesample namespace" << std::endl;
}
}
By placing your version of method1() in the namespace codesample, it is isolated from the method1() function provided by the third-party library.
To call the namespace-enabled version of method1(), prepend the namespace onto the function name by using ::, also called the scope resolution operator, as follows.
codesample::method(); // Calls the "method1" function in the "codesample" namespace.
Any code that falls within a "codesample" namespace block can call other code within the same namespace without explicitly prepending the namespace. This implicit namespace is useful in making the code more readable.
You can also avoid prepending of namespaces with the using directive. This directive tells the compiler that the subsequent code is making use of names in the specified namespace. The namespace is thus implied for the code that follows:
#include "namespaces.h"
using namespace codesample;
int main()
{
method1(); // Implies codesample::method1();
return 0;
}