C++ Logo

std-proposals

Advanced search

[std-proposals] Perfect forwarding for prvalues / expression templates

From: Mital Ashok <mital.vaja_at_[hidden]>
Date: Mon, 25 May 2026 06:12:48 +0100
There is a pattern used sometimes in C++ where you pass a callable
instead of an object. This lets you 'pass' prvalue directly without
materializing an xvalue and moving from it:

    template<class T>
    struct my_optional {
        template<class F>
        T& emplace_result_of(F&& f) {
            reset();
            return ::new (storage) T(std::forward<F>(f)());
        }
    };

    my_optional<std::atomic<int>> a;
    m.emplace_result_of([]{ return std::atomic<int>{1}; });

I would like to propose a standardisation of this that achieves this
by doing something similar to automatically wrapping an expression in
a lambda.

`std::lazy<T>` (name to be changed): Special initialization rules,
when you initialize it, it is like you are initializing a T. It will
have a `T operator()() &&;` that when called will evaluate the
initializer. There is no lifetime extension for anything, as it is
supposed to work similarly to a lambda that has a default capture of
`&` and it is meant to be a function argument. Though in principle
there would be nothing stopping something like:

    std::lazy<int> x = f(); // f is not called here
    return std::move(x)(); // f is called

So for example:

    template<class T>
    struct my_optional {
        T& emplace_lazy(std::lazy<T> x) {
            reset();
            return ::new (storage) T(std::move(x)());
        }
    };

    my_optional<std::atomic<int>> m;
    m.emplace_lazy(1);

This also has a use case for conditionally evaluating something, so
`assert()` could be implemented without a macro, or `logger.log(level,
expression)` could conditionally evaluate the expression based on the
runtime log level.

This could be seen as a potential footgun (operator() could be called
0, 1 or more times), but with the 'standard' use case of just perfect
forwarding, it should be safe enough (similar to how `f(std::move(x))`
might not actually move-from x). Having to call the operator() after
moving should allow existing compiler warnings about use-after-move to
fire.

Further extension is `std::lazy<T>` for a template parameter T
deducing as a non-reference for a prvalue argument and a lvalue or
rvalue reference for a glvalue argument, for forwarding value category
for prvalues better (as opposed to universal references which always
forward prvalues as xvalues).

I'm working on a proof-of-concept Clang implementation of this. I was
also wondering if there have been similar proposals for this in the
past? Sort of similar: <htttps://wg21.link/P2785R3>,
<https://wg21.link/P0573R2>

It will be specified as:

    namespace std {
      template<class T> class lazy {
      public:
        constexpr T operator()() &&;
      };
    }

+ a bunch of cases in [dcl.init] for specializations of (cv?)
std::lazy<T> (but not std::lazy<T>&)

And my implementation is going to have:

template<class T> class lazy {
    T(* f)(void*); // Compiler will directly initialize
    void* ctx;
public:
    constexpr T operator()() && {
        return f(ctx);
    }
};

Received on 2026-05-25 05:13:27