Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A using declaration introduces a specific name from a namespace, base class, or enumeration into the current declarative region. It creates a local synonym for an existing entity, allowing the compiler to resolve the unqualified identifier without requiring the fully qualified nested-name-specifier. Unlike a using directive (using namespace N;), which makes all names in a namespace available, a using declaration explicitly imports only a single specified name (or an overload set).

Syntax

using nested-name-specifier unqualified-id;
using typename nested-name-specifier unqualified-id; // For dependent types
using nested-name-specifier unqualified-id...;       // C++17 pack expansion
  • nested-name-specifier: A sequence of names and scope resolution operators (e.g., std:: or Base<T>::) that specify the namespace or class where the name is originally declared. Note that the trailing :: is inherently part of the nested-name-specifier grammar.
  • unqualified-id: The specific identifier, operator function, or conversion function being introduced.
  • typename: Required when the nested-name-specifier is dependent on a template parameter and the unqualified-id refers to a type.
  • ...: C++17 syntax for expanding a parameter pack.

Scope and Resolution Mechanics

When a using declaration is evaluated, the introduced name becomes a member of the declarative region in which the using declaration appears.
  • Shadowing: The introduced name hides identical names declared in enclosing outer scopes.
  • Collisions: It is ill-formed to introduce a name via a using declaration if a declaration for a different entity with the same name already exists in the exact same local scope, unless both are functions or function templates that form a valid overload set. Conversely, introducing the exact same entity multiple times via using declarations in the same scope is perfectly valid.
namespace System {
    void allocate();
    int status;
}

void process() {
    void allocate(); // Local declaration (different entity, same name)
    // using System::allocate; // Error: conflicts with local declaration
    
    using System::status;
    using System::status; // Valid: introduces the exact same entity multiple times
}

Overload Sets

If the unqualified-id refers to a function, the using declaration introduces all overloaded functions with that name from the target scope. If the target scope adds new overloads after the using declaration, those new overloads are not added to the local scope.
namespace Math {
    void compute(int);
    void compute(double);
}

void execute() {
    using Math::compute; // Imports both void compute(int) and void compute(double)
    compute(42);         // Resolves to Math::compute(int)
}

Class Scope and Inheritance

Within a class definition, a using declaration can introduce a member of a base class into the derived class’s scope. This alters the standard name lookup rules and access control.
  1. Unhiding: It prevents derived class members from hiding base class members with the same name but different signatures.
  2. Access Specification: The access level (public, protected, private) of the introduced name is determined by the access specifier applied to the using declaration in the derived class, overriding the inherited access level.
class Base {
protected:
    void execute(int);
};

class Derived : private Base {
public:
    // Elevates the inherited private member to public visibility in Derived
    // and prevents void execute(double) from hiding void execute(int)
    using Base::execute; 
    
    void execute(double);
};

Dependent Types

When introducing a type from a dependent base class inside a class template, the typename keyword is mandatory. This disambiguates the introduced name, instructing the compiler to treat it as a type rather than a non-type member.
template <typename T>
struct Base {
    typedef T value_type;
};

template <typename T>
struct Derived : Base<T> {
    using typename Base<T>::value_type; // 'typename' is required
    value_type data;
};

Variadic Using Declarations (C++17)

C++17 permits using declarations to expand parameter packs. This is primarily utilized in class templates inheriting from a variadic pack of base classes, allowing the introduction of members from all base classes simultaneously.
template <typename... Bases>
struct Overloader : Bases... {
    using Bases::operator()...; // Expands to introduce operator() from all Bases
};

Enumerations (C++20)

C++20 extends the syntax to support scoped enumerations via using enum. This introduces all enumerators of the specified enumeration into the current declarative region.
enum class Status { Pending, Active, Faulted };

struct Task {
    using enum Status; // Introduces Pending, Active, and Faulted into Task scope
    Status current_status = Pending; 
};
Master C++ with Deep Grasping Methodology!Learn More