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.

The & token in C++ is a context-dependent symbol that serves five distinct syntactic and semantic roles: as a unary address-of operator, an lvalue reference declarator, a binary bitwise AND operator, a lambda capture specifier, and a member function reference qualifier.

1. Unary Address-of Operator

When used as a prefix unary operator, & retrieves the memory address of its operand or forms a pointer to a member.
  • Operand Requirement: The operand must either be an lvalue (an expression designating a function or an object with an identifiable memory location) or a qualified-id designating a non-static class member. It cannot be applied to prvalues (temporary values) or bit-fields.
  • Type Yielded:
    • If the operand is an lvalue of type T, the expression &operand evaluates to a prvalue pointer of type T*.
    • If the operand is a qualified-id for a non-static member of type T in class C (e.g., C::member), the expression &C::member evaluates to a prvalue pointer-to-member of type T C::*.
  • Mechanics: The address is an inherent property of the object’s storage or the member’s offset. It is resolved by the compiler, the linker, or calculated at runtime.
// Standard address-of
int object = 42;
int* pointer_to_object = &object; 

// Pointer-to-member
struct Data { int value; };
int Data::* pointer_to_member = &Data::value;

2. Lvalue Reference Declarator

When appended to a type specifier in a declaration, & acts as a declarator modifier rather than an expression operator. It defines an lvalue reference, which is a structural alias to an existing object.
  • Binding: A reference binds permanently to its referent and cannot be reseated. While it typically requires an initializer at the point of declaration, an initializer is omitted in contexts such as extern declarations, function parameters, and class data members (which are initialized later via constructor member initializer lists).
  • Memory: Unlike pointers, a reference does not necessarily occupy its own distinct memory address; the compiler often optimizes it out, treating it as a direct alias to the original lvalue.
  • Type Yielded: Declares a variable, parameter, or return type of type T&.
// Standard initialization
int original_object = 10;
int& reference_to_object = original_object; 

// Uninitialized at declaration point
extern int& external_ref;
void function(int& parameter_ref);

3. Binary Bitwise AND Operator

When placed between two operands, & functions as a binary operator that performs a bit-by-bit logical AND operation.
  • Operand Requirement: Both operands must be of integral types or unscoped enumeration types. If the operands have different types, standard integer promotions and usual arithmetic conversions are applied.
  • Mechanics: The operator compares each corresponding bit of the two operands. The resulting bit is set to 1 if and only if both corresponding bits in the operands are 1. Otherwise, the resulting bit is 0.
  • Type Yielded: Returns a prvalue of the promoted common integral type.
unsigned int operand_a = 0b1100;
unsigned int operand_b = 0b1010;
unsigned int result = operand_a & operand_b; // Evaluates to 0b1000

4. Lambda Captures

Within the introducer [] of a lambda expression, & dictates how variables from the enclosing scope are captured by the closure type.
  • Capture-default: Using [&] specifies that all implicitly referenced automatic variables from the enclosing scope are captured by lvalue reference.
  • Specific Capture: Using [&x] explicitly captures only the variable x by lvalue reference.
int counter = 0;
auto increment = [&counter]() {
    ++counter; // Modifies the original 'counter' variable
};

5. Member Function Reference Qualifiers

When appended to a non-static member function declaration, & acts as a reference qualifier. It restricts the function so that it can only be invoked on lvalue object instances.
  • Mechanics: It modifies the value category of the implicit object parameter used during overload resolution, requiring it to be an lvalue. It does not modify the this keyword, which always remains a prvalue pointer (e.g., X* or const X*). This qualifier prevents the function from being selected by overload resolution when called on temporary (rvalue) objects.
struct Data {
    void process() & {
        // Implementation
    }
};

Data d;
d.process();       // Valid: 'd' is an lvalue
// Data().process(); // Error: 'Data()' is a prvalue (rvalue)

Operator Overloading and std::addressof

Both the unary address-of and binary bitwise AND variants of the & operator can be overloaded for user-defined types (classes and structs). The reference declarator, lambda capture, and reference qualifier variants are core language constructs and cannot be overloaded. When the unary address-of operator is overloaded, applying & to an object of that type will invoke the custom implementation rather than returning the actual memory address. To safely and reliably obtain the true memory address of an object, bypassing any overloaded operator&, C++ provides std::addressof (defined in the <memory> header).
#include <memory>

class CustomType {
public:
    // Overloading the unary address-of operator
    CustomType* operator&() {
        return nullptr; // Artificial behavior
    }

    // Overloading the binary bitwise AND operator
    CustomType operator&(const CustomType& rhs) const {
        return CustomType();
    }
};

CustomType obj;
CustomType* overloaded_ptr = &obj;                  // Calls operator&, returns nullptr
CustomType* actual_ptr = std::addressof(obj);       // Bypasses overload, returns true memory address
Master C++ with Deep Grasping Methodology!Learn More