Document number:   N3727
Date:   2013-08-01
Project:   Programming Language C++, Library Working Group
Reply-to:  
Tomasz Kamiński <tomaszkam at gmail dot com>

A proposal to add invoke function template

Introduction

The aim of this proposal is to introduce the function template invoke that models INVOKE expression.

Motivation and Scope

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 example

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)...); };
  }

Design Decisions

constexpr implementation

Although 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.

Removal of INVOKE expression

The 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.

Impact On The Standard

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.

Proposed wording

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).

Implementability

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.

Acknowledgements

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.

References

  1. Tomasz Kamiński, Implementation of invoke function (https://github.com/tomaszkam/proposals/blob/master/invoke/invoke_cpp11.hpp)