Concepts / Scope Resolution and Qualified Names

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.

  • Programming

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?

  • Yes, because the variable is in the same namespace

  • No, because a namespace member name is only in scope from its point of declaration onward

Reveal answer
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

Definition

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.

Definition

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.

Key point

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.

Shapes namespacescope of scale ends herescalenamespace member nameradiusclass member nameCircle classscope runs from openingbrace to closing bracedoubledmember function bodydeclarescontainsdeclaresdeclares
Which names can code inside the class see, and where does that visibility stop? The class scope nests inside the namespace scope, and the class member name radius is in scope across the whole class body.

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.

scale declaredscope starts heredoubled declaredinside scope of scaleShapes namespace endsscope of scale ends herelater linesstill in namespace
From which later lines can the namespace member scale be used without qualification? Its scope starts at its declaration and ends at the closing brace of the Shapes namespace.

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.

Code walkthrough
namespace Shapes {
    double scale = 2.0;

    class Circle {
    public:
        double radius;
        double doubled() { return radius * scale; }
    };
}
  1. 1
    Line 2 declares scale in the namespace. Its scope starts at this declaration and runs to the closing brace of the Shapes namespace.
  2. 2
    Line 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.
  3. 3
    Line 6 declares radius as a class member name. It is in scope across the whole class body.
  4. 4
    Line 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
Worked example

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.

  1. 1
    Classify 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.

  2. 2
    Classify radius and doubled

    radius and doubled are defined in the class Circle and are not inside a function body. They are class member names.

  3. 3
    Check 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.

  4. 4
    Check 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.

  5. 5
    Check 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.

Result

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.

FeatureClass member nameNamespace member name
Defined inA classA namespace
Not insideA function, lambda, or enum classA function, lambda, class, or enum class
Scope starts atOpening brace of the enclosing classPoint of declaration
Scope ends atEnd of the class declarationEnd of the namespace
Earlier declarations covered?Yes, the whole class body is coveredNo, only from the declaration onward

Side-by-side rules for the scope of each kind of member name

Defined in classDefined in namespaceOpening bracescope startPoint of declarationscope startClosing brace ofclassscope endEnd of namespacescope endscope startsscope endsscope startsscope ends
Given a declared name, what determines whether it is a class member or a namespace member, and how do the two categories differ in where they can be used?

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 like

    A function declared above scale tries to use scale by its bare name.

    Why it fails

    The scope of a namespace member name starts at its point of declaration, so earlier declarations are not covered.

    Do this instead

    Move 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 like

    A member function placed above radius in the class body uses radius.

    Why it fails

    A class member name's scope covers the whole class body, from the opening brace to the end of the class declaration.

    Do this instead

    Use 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 like

    Code placed after the closing brace of Circle uses radius by its bare name.

    Why it fails

    The scope of a class member name does not extend beyond the closing brace of the class.

    Do this instead

    Qualify 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 like

    A local variable declared inside a member function is treated as a class member name.

    Why it fails

    Names declared inside a function, lambda, or enum class are excluded from class member status.

    Do this instead

    Classify the name by where it is declared. A variable inside a member function is not a class member name.

Practice Tracing Names

MEDIUM

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.

Related concepts

More concepts starting with S