Skip to content

Forward a __tuple's rvalue-reference elements as rvalues - #2375

Open
ispeters wants to merge 1 commit into
NVIDIA:mainfrom
ispeters:perfectly-forward-tuple
Open

ispeters wants to merge 1 commit into
NVIDIA:mainfrom
ispeters:perfectly-forward-tuple

Conversation

@ispeters

Copy link
Copy Markdown
Contributor

__apply and __get pass on each element of a forwarded __tuple by naming it as a member of the forwarded tuple, but naming a member of reference type gives an lvalue whatever the tuple's value category. So an element declared as T && is passed on from a moved tuple as T &, although __apply's constraint, noexcept and return type describe it as T &&, and __get already returns it as T && from a tuple of more than eight elements. A function that can take only an rvalue can't be applied, and one that takes the element by value copies it instead of moving it.

exec::function stores its curried arguments in a __tuple<_Args...>, so a function whose call type takes a curried argument of rvalue reference type, such as function<std::string(std::string &&)>, can be constructed but not connected: connect applies to the moved tuple a function that takes the argument as std::string &&.

This diff casts each element to its declared type with the forwarded tuple's cvref-qualifiers, as std::get does and as __structured_apply already does, in __apply for tuples of every size and in __get for tuples of up to eight elements. Elements of value or lvalue reference type are passed on as before.

The new tests check what __apply and __get give for elements of value, lvalue reference and rvalue reference type, from small and large tuples, and that a function passes its curried arguments to the factory as their declared types.

@copy-pr-bot

copy-pr-bot Bot commented Oct 28, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

`__apply` and `__get` pass on each element of a forwarded `__tuple` by
naming it as a member of the forwarded tuple, but naming a member of
reference type gives an lvalue whatever the tuple's value category. So
an element declared as `T &&` is passed on from a moved tuple as `T &`,
although `__apply`'s constraint, `noexcept` and return type describe it
as `T &&`, and `__get` already returns it as `T &&` from a tuple of more
than eight elements. A function that can take only an rvalue can't be
applied, and one that takes the element by value copies it instead of
moving it.

`exec::function` stores its curried arguments in a `__tuple<_Args...>`,
so a `function` whose call type takes a curried argument of rvalue
reference type, such as `function<std::string(std::string &&)>`, can be
constructed but not connected: `connect` applies to the moved tuple a
function that takes the argument as `std::string &&`.

This diff casts each element to its declared type with the forwarded
tuple's cvref-qualifiers, as `std::get` does and as
`__structured_apply` already does, in `__apply` for tuples of every size
and in `__get` for tuples of up to eight elements. Elements of value or
lvalue reference type are passed on as before.

The new tests check what `__apply` and `__get` give for elements of
value, lvalue reference and rvalue reference type, from small and large
tuples, and that a `function` passes its curried arguments to the
factory as their declared types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ispeters
ispeters force-pushed the perfectly-forward-tuple branch from 70d2df8 to 21c9bb9 Compare October 29, 2026 02:31

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant