Document number: | N3727 | |
---|---|---|
Date: | 2013-08-01 | |
Project: | Programming Language C++, Library Working Group | |
Reply-to: | Tomasz Kamiński <tomaszkam at gmail dot com> |
The aim of this proposal is to introduce the function template invoke
that models INVOKE
expression.
Although the behaviour of the INVOKE
expression may be reproduced by combination of the existing standard library components, separate treatment of the functors and member pointers is required in such solutions.
deref_fn
exampleAs example, please consider the deref_fn
wrapper, that should accept any callable object f
and return a functor that invokes provided callable and dereferences the result.
Using existing C++14 standard facilities deref_fn
function may be implemented as:
template<typename F, std::enable_if_t<!std::is_member_pointer<std::decay_t<F>>{}, int> == 0> auto deref_fn(F&& f) { return [f](auto&&... args) { return *f(std::forward<decltype(args)>(args)...); }; } template<typename F, std::enable_if_t<std::is_member_pointer<std::decay_t<F>>{}, int> == 0> auto deref_fn(F&& f) { return [mf = std::mem_fn(f)](auto&&... args) { return *mf(std::forward<decltype(args)>(args)...); }; }
Proposed invoke
function allows simpler implementation, that does not resort to use of SFINAE function overloading:
template<typename F> auto deref_fn(F&& f) { return [f](auto&&... args) { return *std::invoke(f, std::forward<decltype(args)>(args)...); }; }
constexpr
implementationAlthough there is possibility to implement standard conforming invoke
function template as a constexpr
function, the proposed wording does not require such implementation.
The main reason is to left it consistent with existing standard function objects, that could have such definition, like std::mem_fn
, std::reference_wrapper
and operator wrappers.
Furthermore imposing such requirement will block the implementation of invoke
that refers to std::mem_fn
.
This proposal assumes that constexpr
addition to the <functional>
header would be applied consistently by a separate proposal.
Both constexpr
and standard library based implementation are presented in Implementability section of the proposal.
INVOKE
expressionThe wording of this proposal may also be specified by moving the requirements of the INVOKE
expression to definition of the invoke
function template and replacing all existing references to the INVOKE
expression by corresponding invoke
function invocation.
The approach described above is not used in this proposal, primarily to give implementation freedom to the standard library providers (e.g., whether invoke
should be defined in terms of mem_fn
or vice versa).
In addition such approach will require changes in existing implementations of standard function objects, without providing any real benefit from the library user perspective.
This proposal has no dependencies beyond a C++11 compiler and Standard Library implementation. (It depends on perfect forwarding, varidatic templates, decltype
and trailing return types.)
Nothing depends on this proposal.
After the declaration of binary_function
in the section 20.10 [function.objects]/2 (Header <functional>
synopsis), add:
// 20.10.3, invoke template <class F, class... Args> typename result_of<F&&(Args&&...)>::type invoke(F&& f, Args&&... args); template <class R, class F, class... Args> R invoke(F&& f, Args&&... args);
After paragraph 20.10.2 Requirements [func.require], insert a new paragraph. (Chapter [refwrap] (Class template reference_wrapper
) becomes 20.10.?)
20.10.3 Function template
invoke
[invoke]template <class F, class... Args> typename result_of<F&&(Args&&...)>::type invoke(F&& f, Args&&... args);
- Returns:
INVOKE(std::forward<F>(f), std::forward<Args>(args)...)
([func.require] 20.10.2).template <class R, class F, class... Args> R invoke(F&& f, Args&&... args);
- Returns:
INVOKE(std::forward<F>(f), std::forward<Args>(args)..., R)
([func.require] 20.10.2).
Proposed invoke
function template may be implemented in terms of existing C++11 standard library components:
template<typename Functor, typename... Args> typename std::enable_if< std::is_member_pointer<typename std::decay<Functor>::type>::value, typename std::result_of<Functor&&(Args&&...)>::type >::type invoke(Functor&& f, Args&&... args) { return std::mem_fn(f)(std::forward<Args>(args)...); } template<typename Functor, typename... Args> typename std::enable_if< !std::is_member_pointer<typename std::decay<Functor>::type>::value, typename std::result_of<Functor&&(Args&&...)>::type >::type invoke(Functor&& f, Args&&... args) { return std::forward<Functor>(f)(std::forward<Args>(args)...); } template<typename Return, typename Functor, typename... Args> Return invoke(Functor&& f, Args&&... args) { return invoke(std::forward<Functor>(f), std::forward<Args>(args)...); }
An constexpr
implemenatation may be found at: https://github.com/tomaszkam/proposals/blob/master/invoke/invoke_cpp11.hpp.
Joe Gottman originally proposed invoke
function in discussion group ISO C++ Standard - Future Proposals.
Andrzej Krzemieński offered many useful suggestions and corrections to the proposal.