On Wed, Mar 5, 2025 at 6:44 AM Avi Kivity <avi@scylladb.com> wrote:
On Tue, 2025-03-04 at 12:44 -0500, Arthur O'Dwyer wrote:
> On Tue, Mar 4, 2025 at 10:01 AM Avi Kivity wrote:
>  
> > If all constructor parameters are rvalues, then the range produces
> > rvalues too.
>
> What if some constructor arguments aren't rvalues? It'd have to be
> ill-formed then, right?

No.

> At any rate, you can't have the return type of
> `ranges::literal<T>::iterator::operator*()` dynamically depend on how
> the `ranges::literal<T>` object was constructed.

Why not? If ranges::literal<T> is a function object, different
overloads of literal::operator() will be called, resulting in different
return value types.

(A moot point now, sounds like...)

Even if `ranges::literal<T>` were to hold a function pointer inside itself, like,

template<class T>
struct literal {
    ~~~~
    FP deref_ = nullptr;
    template<class... Args> literal(Args&&... args) {
        ~~~~
        deref_ = (is_reference_v<Args> || ...) ?
          +[](T& x) { return x; } :
          +[](T& x) { return std::move(x); };
    }
};

that still wouldn't let you change the type of literal<T>::iterator::operator*() at runtime.
You would instead have to have two different compile-time kinds of literal<T> — for example,

template<class T, bool AreAllRvalues>
struct literal { ~~~~ };

template<class... Args>
auto make_literal(Args&&... args) {
  if constexpr ((is_reference_v<Args> || ...)) {
    return literal<T, false>(args...);
  } else {
    return literal<T, true>(args...);
  }
}

int x = 2;
ranges::literal<int, true> lit1 = ranges::make_literal(1,2,3);
ranges::literal<int, false> lit1 = ranges::make_literal(1,x,3);

–Arthur