C++ 17 a perfect implementation of forwarding a function call

  • 2020-05-27 06:51:15
  • OfStack

preface

This article mainly introduced to you about C++17 forward 1 function call related content, to share out for your reference to learn, the following words do not say, to 1 look at the detailed introduction.

Methods the following

First you have a flash of inspiration:


#define WARP_CALL(fun, ...) fun(__VA_ARGS__)

No, we don't like macros. They're too extensible


template<class R, class T1, class T2, class T3>
R warp_call(R(*fun)(T1, T2, T3), T1 a, T2 b, T3 c)
{
 return fun(a, b, c);
}

If you wrote the code above, you must have transferred it from C, C++ is not yet ripe. Consider callable object and C++11 variadic template features:


template<class Fun, class... Args>
auto wrap_call(Fun f, Args... args) -> decltype(f(args...))
{
 return f(args...);
}

With the mobile semantics, the return value is derived:


template<class Fun, class... Args>
auto wrap_call(Fun&& f, Args&&... args)
{
 return std::forward<Fun>(f)(std::forward<Args>(args)...);
}

The auto return value actually has the problem of the parameter being decay, with decltype + trailing the return value


template<class Fun, class... Args>
auto wrap_call(Fun&& f, Args&&... args)
 -> decltype(std::forward<Fun>(f)(std::forward<Args>(args)...))
{
 return std::forward<Fun>(f)(std::forward<Args>(args)...);
}

With C++14, you can directly use decltype(auto)


template<class Fun, class... Args>
decltype(auto) wrap_call(Fun&& f, Args&&... args)
{
 return std::forward<Fun>(f)(std::forward<Args>(args)...);
}

Don't forget to noexcept


template<class Fun, class... Args>
decltype(auto) wrap_call(Fun&& f, Args&&... args)
 noexcept(noexcept(std::forward<Fun>(f)(std::forward<Args>(args)...)))
{
 return std::forward<Fun>(f)(std::forward<Args>(args)...);
}

However, the above function is not SFINAE-friendly, because the return value of decltype(auto) cannot be derived directly from the function signature, while the return value derivation of this function may result in hard error interrupting SFINAE. So it's best to write the return value manually


template<class Fun, class... Args>
auto wrap_call(Fun&& f, Args&&... args)
 noexcept(noexcept(std::forward<Fun>(f)(std::forward<Args>(args)...)))
 -> decltype(std::forward<Fun>(f)(std::forward<Args>(args)...))
{
 return std::forward<Fun>(f)(std::forward<Args>(args)...);
}

What else are we missing? constexpr


template<class Fun, class... Args>
constexpr auto wrap_call(Fun&& f, Args&&... args)
 noexcept(noexcept(std::forward<Fun>(f)(std::forward<Args>(args)...)))
 -> decltype(std::forward<Fun>(f)(std::forward<Args>(args)...))
{
 return std::forward<Fun>(f)(std::forward<Args>(args)...);
}

It's perfect up here

Perfect? Look at the std: : invoke

conclusion


Related articles: