Scope Resolution and Qualified Names
When reasoning about a name, first decide whether it is a class member name or a namespace member name. Then apply the matching start and end points for its scope.
Why Declaration Location Decides Usability
Every name in a C++ program is declared somewhere, and the place where it is declared determines where that name can be used. Two of the most common homes for names are classes and namespaces. A name declared inside a class is different from a name declared inside a namespace, and both differ from a name declared inside a function. This article focuses on the first two: what makes a name a class member name or a namespace member name, and how far each one reaches in the program.
The question this article answers is simple to state: given a name in a piece of code, where is it usable? Keep that question in mind as you read. The rules you will learn are precise, and each one comes down to two points: where the scope of a name starts, and where it ends.
What do you think happens?
In a namespace, a function is declared before a variable it tries to use. Can the function refer to that variable by its bare name?
Reveal answer
No, because a namespace member name is only in scope from its point of declaration onward
A namespace member name's scope begins at its point of declaration and continues to the end of the namespace. Declarations that appear before it are not covered by its scope, so the order of declarations inside a namespace controls which names the earlier declarations can use.
Two Kinds of Member Names
A class member name is a name defined in a class, outside any function, lambda, or enum class. Its scope runs from the opening brace of the enclosing declaration to the end of that declaration.
A namespace member name is a name defined in a namespace, outside any function, lambda, class, or enum class. Its scope runs from its point of declaration to the end of its namespace.
The two definitions share a structure. Each one says where the name is defined, and then says where its scope lies. Notice the exclusions as well: a name declared inside a function, a lambda, or an enum class is not a class member name or a namespace member name at all. A local variable inside a member function, for example, does not become a class member just because it sits inside a class.
When reasoning about a name, first decide whether it is a class member name or a namespace member name. Then apply the matching start and end points for its scope.
Scope of a Class Member Name
A class member name belongs to the declaration that encloses it, which is the class itself. Its scope therefore starts at the opening brace of that class and runs until the class declaration ends. Across the whole class body, the name is in scope from the top, so its position among the other declarations of the class does not limit where it can be used inside that body.
The name does not reach beyond the closing brace of the class. Code placed after the class cannot refer to the member name by its bare name in the same way code inside the class can. The diagram below shows how the class scope sits inside the namespace and how a member name sits inside the class.
Scope of a Namespace Member Name
A namespace member name is declared directly in a namespace, which makes it a common target for other parts of a program that want to use a grouped set of related declarations. Its scope begins only where it is declared, so the earlier part of the namespace is not covered. It then continues to the end of the namespace.
Because the start point is the declaration itself, the order of declarations inside a namespace controls which names the later declarations can use. A declaration that comes before a namespace member name is not inside that name's scope, so it cannot rely on the name by its bare name.
Tracing Names in the Shapes Example
The example below was written for this article to show both kinds of member name in one place. Read it with the definitions in mind and identify which names are namespace member names and which are class member names before reading the walkthrough.
namespace Shapes {
double scale = 2.0;
class Circle {
public:
double radius;
double doubled() { return radius * scale; }
};
}- 1Line 2 declares scale in the namespace. Its scope starts at this declaration and runs to the closing brace of the Shapes namespace.
- 2Line 4 starts the Circle class. The class member names radius and doubled have scopes that start at the opening brace of Circle and end at the closing brace of Circle.
- 3Line 6 declares radius as a class member name. It is in scope across the whole class body.
- 4Line 7 declares doubled. Its body uses radius, which is a class member name, and scale, which is a namespace member name declared earlier in the namespace. Because scale is declared before doubled, doubled can use it.
- scale is declared directly in the Shapes namespace, so it is a namespace member name
- radius is declared directly in the Circle class, so it is a class member name
- doubled is also a class member name, declared inside the Circle class
Deciding Where Each Name Is Usable
Using the Shapes example, decide for each name whether it is a class member name or a namespace member name, and whether a bare-name use in doubled is allowed.
- 1Classify scale
scale is defined in the namespace Shapes and is not inside any function, lambda, class, or enum class. It is a namespace member name.
- 2Classify radius and doubled
radius and doubled are defined in the class Circle and are not inside a function body. They are class member names.
- 3Check the use of scale inside doubled
scale is declared before doubled, so the point where scale begins to be in scope comes before the declaration of doubled. The bare name scale is therefore usable there.
- 4Check the use of radius inside doubled
radius is a class member name, so its scope covers the whole body of Circle from the opening brace to the closing brace. The bare name radius is usable inside doubled.
- 5Check a use after the class
Code placed after the closing brace of Circle is outside the scope of radius, so it cannot refer to radius by its bare name in the same way code inside the class can.
scale is a namespace member name usable from its declaration onward, radius is a class member name usable across the class body, and the bare name radius does not reach past the class's closing brace.
Comparing the Two Scopes
The most important difference between the two kinds of member name is the starting point. A class member name's scope begins at the opening brace of its class, so the whole class body is covered from the top. A namespace member name's scope begins only where it is declared, so the earlier part of the namespace is not covered. Both scopes end at the end of their enclosing construct.
| Feature | Class member name | Namespace member name |
|---|---|---|
| Defined in | A class | A namespace |
| Not inside | A function, lambda, or enum class | A function, lambda, class, or enum class |
| Scope starts at | Opening brace of the enclosing class | Point of declaration |
| Scope ends at | End of the class declaration | End of the namespace |
| Earlier declarations covered? | Yes, the whole class body is covered | No, only from the declaration onward |
Side-by-side rules for the scope of each kind of member name
Qualified Names in Brief
A qualified name spells out the enclosing name in front of the member, such as Shapes::scale for the namespace member scale or Circle::radius for the class member radius. Writing the enclosing name tells the compiler which declaration to search for. This article does not go deeper into qualified-name rules, because the scope rules above are what determine where a name can be used. Use qualified names when you need to refer to a member from a place where its bare name is not in scope.
Mistakes About When a Name Is Usable
Common mistake Assuming a namespace member name is usable before its declaration
What it looks likeA function declared above scale tries to use scale by its bare name.
Why it failsThe scope of a namespace member name starts at its point of declaration, so earlier declarations are not covered.
Do this insteadMove the declaration above the code that uses it, or refer to the name in a way that matches its scope.
Common mistake Assuming a class member name is only usable below its declaration inside the class
What it looks likeA member function placed above radius in the class body uses radius.
Why it failsA class member name's scope covers the whole class body, from the opening brace to the end of the class declaration.
Do this insteadUse the rule for class members: the whole class body is covered, so the position of the declaration within the class does not limit use inside that body.
Common mistake Assuming a class member name is usable after the class ends
What it looks likeCode placed after the closing brace of Circle uses radius by its bare name.
Why it failsThe scope of a class member name does not extend beyond the closing brace of the class.
Do this insteadQualify the name, or use it from inside the class scope.
Common mistake Treating every declaration inside a class as a class member name
What it looks likeA local variable declared inside a member function is treated as a class member name.
Why it failsNames declared inside a function, lambda, or enum class are excluded from class member status.
Do this insteadClassify the name by where it is declared. A variable inside a member function is not a class member name.
Practice Tracing Names
Take the Shapes example and add a second namespace member named unit declared after the class Circle closes. Then decide whether a member function of Circle can use unit by its bare name, and explain your answer using the start and end points of each scope.
Hints
- First decide whether unit is a class member name or a namespace member name.
- Check where the scope of unit starts. Is the point of declaration before or after the body of doubled?
- A namespace member name's scope ends at the end of the namespace, not at the end of the class.
Key Takeaways
- A class member name is defined in a class, outside any function, lambda, or enum class, and its scope runs from the opening brace of the enclosing declaration to the end of that declaration.
- A namespace member name is defined in a namespace, outside any function, lambda, class, or enum class, and its scope runs from its point of declaration to the end of its namespace.
- The key difference is the starting point: class member names are in scope across the whole class body, while namespace member names are in scope only from where they are declared onward.
- To decide whether a name is usable, first classify it as a class member name or a namespace member name, then apply the matching start and end points.