Document number: | N4169 | |
---|---|---|
Date: | 2014-08-22 | |
Project: | Programming Language C++, Library Evolution 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 provide uniform semantics for invoking all C++
callable types which includes: function pointers, member pointers and functors.
The behaviour of the function is defined in terms of INVOKE
expression.
Changes since N3727:
invoke<R>
).The functional components defined introduced in the C++11 has native support for member pointers, that means that these types
can be directly used with std::function
or std::bind
, without need to use wrappers like std::mem_fun
,
std::mem_fun_ref
or std::mem_fn
. This uniform support was achieved by introduction of INVOKE
expression.
On the other side, there is no standard component, that will provide uniform syntax for calling any callable object with given arguments,
so when generic component is developed, the programmer has two options: implement separate handling for member pointers (auto boxing with
std::mem_fn
) or support only function types. Because of additional implementation burden and relatively small popularity
of this feature the second approach is preferred. Most notably the apply
function from Library Fundamentals TS
lacks of support for member pointers.
As 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.
First revision of the paper definied second overload of the proposed function (invoke<R>(args...)
), mapped to INVOKE
expression with explicit specification of return type (INVOKE(args..., R)
). Mentioned version is leftover from TR1 implementation,
were result type was determined using result_of
protocol or has to be specified at call side and, after the introduction of type interference
in C++11, it becomes obsolete.
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.9 [function.objects]/2 (Header <functional>
synopsis), add:
// 20.9.3, invoke template <class F, class... Args> result_of_t<F&&(Args&&...)> invoke(F&& f, Args&&... args);
After paragraph 20.9.2 Requirements [func.require], insert a new paragraph. (Chapter [refwrap] (Class template reference_wrapper
) becomes 20.9.?)
20.9.3 Function template
invoke
[func.invoke]template <class F, class... Args> result_of_t<F&&(Args&&...)> invoke(F&& f, Args&&... args);
- Returns:
INVOKE(std::forward<F>(f), std::forward<Args>(args)...)
([func.require] 20.9.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)...); }
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.