Doc. no.: | P2136R0 |
Date: | 2020-3-2 |
Audience: | LEWG |
Reply-to: | Zhihao Yuan <zy at miator dot net> |
invoke<R>
Introduction
This paper proposes invoke<R>
, a variant of std::invoke
that allows specifying the return type, realizing the semantics of INVOKE<R>
rather than INVOKE
.
History
When std::invoke
was introduced in N4169, invoke<R>
was dropped from the proposal with the belief that such a form is unnecessary, stating:
Mentioned version is leftover from TR1 implementation, were the result type was determined using result_of
protocol or has to be specified at the call side and, after the introduction of type interference in C++11, it becomes obsolete.
But in 2015, LWG 2420 was applied to the working draft. INVOKE(f, args…, void)
's capability of casting away the return value is confirmed and specified.
In 2016, the author of this paper opened LWG 2690, proposing std::invoke<R>
. The author of N4169 commented on that issue, confirmed that:
The lack of invoke<R>
was basically a result of the concurrent publication of the never revision of the paper and additional special semantics of INVOKE(f, args…, void)
.
In 2017, INVOKE(f, args…, void)
gained the current spelling INVOKE<R>(f, args…)
in P0604R0. In the same paper, all the new invocation traits get _r
variants that allow specifying the return type.
In 2018, std::visit<R>
is added to the working draft. The usefulness of INVOKE<R>
keeps getting attention.
Discussion
How useful invoke<R>
is?
invoke<R>(...)
does three things that std::invoke(...)
does not:
- In a call forwarder that allows specifying the return type or the full signature, putting
void
as the return type naturally discards the return value, as implied by std::is_invocable_r
and std::is_nothrow_invocable_r
.
- When
R
is not cv void
, you can specify a compatible return type that is different from the callable entity. For example, you can request a function that returns T&&
to return a prvalue of type T
by using invoke<T>
.
- If the callable entity has overloaded call operators that may return different types, they may agree on a return type that allows you to specify.
Can we avoid ambiguity when R
is the callable type?
- We can name it
invoke_r
if we want to, although neither std::bind
nor std::visit
does this.
- We may also prevent the existing
std::invoke
from deducing its first template parameter by prepending a int = 0
(deduction guard) if that does not raise ABI concerns.
- Constraining the new signature is doable but tricky. The wording needs to match value categories.
Wording
The wording is relative to N4849.
Modify 20.14.1 [functional.syn], header <functional>
synopsis, as indicated:
namespace std {
// 20.14.4 [func.invoke], invoke
template<class F, class... Args>
constexpr invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
noexcept(is_nothrow_invocable_v<F, Args...>);
template <class R, class F, class... Args>
constexpr R invoke(F&& f, Args&&... args)
noexcept(is_nothrow_invocable_r_v<R, F, Args...>);
Add the following sequence of paragraphs after 20.14.4 [func.invoke]/1 as indicated:
template <class R, class F, class... Args>
constexpr R invoke(F&& f, Args&&... args)
noexcept(is_nothrow_invocable_r_v<R, F, Args...>);
Constraints: is_invocable_r_v<R, F, Args...>
.
Returns: INVOKE<R>(std::forward<F>(f), std::forward<Args>(args)...)
(20.14.3 [func.require]).
References
invoke<R>
Introduction
This paper proposes
invoke<R>
, a variant ofstd::invoke
that allows specifying the return type, realizing the semantics ofINVOKE<R>
rather thanINVOKE
.History
When
std::invoke
was introduced in N4169[1],invoke<R>
was dropped from the proposal with the belief that such a form is unnecessary, stating:But in 2015, LWG 2420[2] was applied to the working draft.
INVOKE(f, args…, void)
's capability of casting away the return value is confirmed and specified.In 2016, the author of this paper opened LWG 2690[3], proposing
std::invoke<R>
. The author of N4169 commented on that issue, confirmed that:In 2017,
INVOKE(f, args…, void)
gained the current spellingINVOKE<R>(f, args…)
in P0604R0[4]. In the same paper, all the new invocation traits get_r
variants that allow specifying the return type.In 2018,
std::visit<R>
[5] is added to the working draft. The usefulness ofINVOKE<R>
keeps getting attention.Discussion
How useful
invoke<R>
is?invoke<R>(...)
does three things thatstd::invoke(...)
does not:void
as the return type naturally discards the return value, as implied bystd::is_invocable_r
andstd::is_nothrow_invocable_r
.R
is not cvvoid
, you can specify a compatible return type that is different from the callable entity. For example, you can request a function that returnsT&&
to return a prvalue of typeT
by usinginvoke<T>
.Can we avoid ambiguity when
R
is the callable type?invoke_r
if we want to, although neitherstd::bind
norstd::visit
does this.std::invoke
from deducing its first template parameter by prepending aint = 0
(deduction guard) if that does not raise ABI concerns.Wording
The wording is relative to N4849.
Modify 20.14.1 [functional.syn], header
<functional>
synopsis, as indicated:Add the following sequence of paragraphs after 20.14.4 [func.invoke]/1 as indicated:
References
Kamiński, Tomasz. A proposal to add invoke function template (Revision 1). http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2014/n4169.html ↩︎
Bergé, Agustín. LWG 2420
function<void(ArgTypes...)>
does not discard the return value of the target object. https://cplusplus.github.io/LWG/issue2420 ↩︎Yuan, Zhihao. LWG 2690
invoke<R>
. https://cplusplus.github.io/LWG/lwg-active.html#2690 ↩︎Krügler, Daniel et al. Resolving GB 55, US 84, US 85, US 86. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0604r0.html ↩︎
Park, Michael and Agustín Bergé.
visit<R>
: Explicit Return Type forvisit
. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0655r1.pdf ↩︎