CodeNebula
C++26stlexpert

std::function_ref (C++26 Addition)

C++26 · The Next Frontier · Compiler flag: -std=c++26

Explanation

A lightweight, non-owning reference to a callable. Unlike `std::function`, it never allocates and never copies the callable, making it ideal for passing callbacks to functions that do not need to store them beyond the call.

Real-World Scenario

Pass a sorting comparator to a library function without the overhead of type-erased heap allocation.

Quick Snippet

snippet.cpp
#include <functional>

void apply(std::function_ref<int(int)> fn, int value) {
    std::println("Result: {}", fn(value));
}

auto square = [](int x) { return x * x; };
apply(square, 5); // No allocation, no copy -- just a reference

Full Example

function_ref.cpp
// CodeNebula - C++26 Example: std::function_ref
// Compile: g++ -std=c++26 -Wall -Wextra -o example function_ref.cpp
// Note: Requires C++26 compiler support (GCC 15+ / Clang 19+ expected)

// C++26 introduces std::function_ref as a lightweight, non-owning
// reference to a callable. Unlike std::function, it never allocates
// and never copies the callable -- it just references it.

#include <iostream>
#include <functional>
#include <string>
#include <vector>

// --- C++26 std::function_ref Syntax ---
//
// #include <functional>
//
// void apply(std::function_ref<int(int)> fn, int x) {
//     std::cout << fn(x);  // Calls the referenced callable
// }
//
// auto lambda = [](int x) { return x * 2; };
// apply(lambda, 5);  // No copy, no allocation
//
// // Key properties:
// // - Non-owning: does NOT extend lifetime of the callable
// // - Trivially copyable: just a pointer + function pointer internally
// // - Perfect for callback parameters where ownership isn't needed

// --- Simulation using raw function pointers + templates ---

// Simulate function_ref with a lightweight type-erased wrapper
template<typename Sig>
class function_ref_sim;

template<typename R, typename... Args>
class function_ref_sim<R(Args...)> {
    void* obj_ = nullptr;
    R (*invoke_)(void*, Args...) = nullptr;

public:
    // From function pointer
    function_ref_sim(R (*fn)(Args...))
        : obj_(reinterpret_cast<void*>(fn))
        , invoke_([](void* o, Args... args) -> R {
            return reinterpret_cast<R(*)(Args...)>(o)(args...);
        }) {}

    // From any callable (lambda, functor)
    template<typename F>
    function_ref_sim(F& f)
        : obj_(const_cast<void*>(static_cast<const void*>(&f)))
        , invoke_([](void* o, Args... args) -> R {
            return (*static_cast<F*>(o))(args...);
        }) {}

    R operator()(Args... args) const {
        return invoke_(obj_, args...);
    }
};

// A function that takes a non-owning callable reference
void apply_and_print(function_ref_sim<int(int)> fn, int x) {
    std::cout << "  fn(" << x << ") = " << fn(x) << std::endl;
}

int square(int x) { return x * x; }
int negate(int x) { return -x; }

int main() {
    std::cout << "=== C++26 std::function_ref (Simulated) ===" << std::endl;

    // With function pointers
    std::cout << "Function pointers:" << std::endl;
    apply_and_print(square, 5);
    apply_and_print(negate, 3);

    // With lambdas (no allocation, just a reference)
    auto doubler = [](int x) { return x * 2; };
    auto adder = [offset = 100](int x) { return x + offset; };
    std::cout << "Lambdas:" << std::endl;
    apply_and_print(doubler, 7);
    apply_and_print(adder, 5);

    // Show it's non-owning: sizeof is tiny
    std::cout << "\nsizeof(function_ref_sim): " << sizeof(function_ref_sim<int(int)>)
              << " bytes (2 pointers)" << std::endl;
    std::cout << "sizeof(std::function):    " << sizeof(std::function<int(int)>)
              << " bytes" << std::endl;

    std::cout << "\nfunction_ref: zero-overhead non-owning callable reference." << std::endl;
    return 0;
}

// Expected output:
// === C++26 std::function_ref (Simulated) ===
// Function pointers:
//   fn(5) = 25
//   fn(3) = -3
// Lambdas:
//   fn(7) = 14
//   fn(5) = 105
//
// sizeof(function_ref_sim): 16 bytes (2 pointers)
// sizeof(std::function):    32 bytes
//
// function_ref: zero-overhead non-owning callable reference.
Run command
g++ -std=c++26 -Wall -Wextra -o example function_ref.cpp