CodeNebula
C++23stlexpert

std::forward_like (C++23 Addition)

C++23 · Completing Modern · Compiler flag: -std=c++23

Explanation

Forwards arguments with the same value category as a reference.

Real-World Scenario

Preserve value category in a generic forwarding function.

Quick Snippet

snippet.cpp
#include <utility> auto x = std::forward_like<T>(arg);

Full Example

forward_like.cpp
// CodeNebula - C++23 Example: std::forward_like
// Compile: g++ -std=c++23 -Wall -Wextra -o example forward_like.cpp
// Note: Requires GCC 13+ / Clang 17+

#include <iostream>
#include <string>
#include <type_traits>
#include <utility>

// std::forward_like<T>(u) forwards u with the same value category and
// const-qualification as T. This preserves the "ownership" semantics
// of an outer wrapper when accessing inner members.

struct Inner {
    std::string name = "widget";
};

struct Wrapper {
    Inner inner;

    // Without forward_like, accessing inner from an rvalue Wrapper
    // would still yield an lvalue reference. forward_like fixes this.

    // C++23: propagate the value category of *this to inner
    template <typename Self>
    auto&& get(this Self&& self) {
        return std::forward_like<Self>(self.inner);
    }
};

// Demonstrate value category detection
void observe(Inner& /*x*/)        { std::cout << "  -> lvalue ref\n"; }
void observe(const Inner& /*x*/)  { std::cout << "  -> const lvalue ref\n"; }
void observe(Inner&& /*x*/)       { std::cout << "  -> rvalue ref\n"; }
void observe(const Inner&& /*x*/) { std::cout << "  -> const rvalue ref\n"; }

// forward_like in a generic context
template <typename Outer, typename Inner_>
void access_member(Outer&& outer, Inner_ Outer::*member) {
    // Forward the member with the same category as the outer object
    using OuterRef = Outer&&;
    auto&& val = std::forward_like<OuterRef>(outer.*member);
    observe(std::forward<decltype(val)>(val));
}

int main() {
    std::cout << "=== std::forward_like ownership-preserving forwarding ===\n\n";

    Wrapper w;
    const Wrapper cw;

    // Deducing this + forward_like
    std::cout << "w.get() [mutable lvalue]:\n";
    observe(w.get());

    std::cout << "cw.get() [const lvalue]:\n";
    observe(cw.get());

    std::cout << "Wrapper{}.get() [rvalue]:\n";
    observe(Wrapper{}.get());

    std::cout << "std::move(cw).get() [const rvalue]:\n";
    observe(std::move(cw).get());

    std::cout << "\n--- Direct forward_like examples ---\n";

    Inner inner;
    std::cout << "forward_like<int&>(inner):        ";
    observe(std::forward_like<int&>(inner));

    std::cout << "forward_like<const int&>(inner):  ";
    observe(std::forward_like<const int&>(inner));

    std::cout << "forward_like<int&&>(inner):       ";
    observe(std::forward_like<int&&>(inner));

    std::cout << "forward_like<const int&&>(inner): ";
    observe(std::forward_like<const int&&>(inner));

    return 0;
}

// Expected output:
// === std::forward_like ownership-preserving forwarding ===
//
// w.get() [mutable lvalue]:
//   -> lvalue ref
// cw.get() [const lvalue]:
//   -> const lvalue ref
// Wrapper{}.get() [rvalue]:
//   -> rvalue ref
// std::move(cw).get() [const rvalue]:
//   -> const rvalue ref
//
// --- Direct forward_like examples ---
// forward_like<int&>(inner):        -> lvalue ref
// forward_like<const int&>(inner):  -> const lvalue ref
// forward_like<int&&>(inner):       -> rvalue ref
// forward_like<const int&&>(inner): -> const rvalue ref
Run command
g++ -std=c++23 -Wall -Wextra -o example forward_like.cpp