C++17 introduces std::invoke
which calls the exposition-only INVOKE
. Here is an implementation. This isn't a large amount of code, and it is to a large extent a transliteration of [func.require].
I took the liberty to add invoke_r
which calls INVOKE<R>
since I find it quite useful at times.
// C++17 std::invoke implementation
#ifndef INC_INVOKE_HPP_6AY08xPaOo
#define INC_INVOKE_HPP_6AY08xPaOo
#include <functional>
#include <type_traits>
#include <utility>
namespace my_std {
// 23.14.4 Function template invoke [func.invoke]
// Returns: INVOKE(std::forward<F>(f), std::forward<Args>(args)...)
// (See 23.14.3 Requirements [func.require] Paragraph 1)
template <typename F, typename... Args>
std::invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
noexcept(std::is_nothrow_invocable_v<F, Args...>);
// Added functionality
// Returns: INVOKE<R>(std::forward<F>(f), std::forward<Args>(args)...)
// (See 23.14.3 Requirements [func.require] Paragraph 2)
template <typename R, typename F, typename... Args>
R invoke_r(F&& f, Args&&... args)
noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>);
namespace detail {
template <typename T>
struct is_reference_wrapper :std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>>
:std::true_type {};
template <typename T>
inline constexpr bool is_reference_wrapper_v =
is_reference_wrapper<T>::value;
template <typename F, typename C, typename T, typename... Args>
decltype(auto) invoke_mfp(F C::*f, T&& t, Args&&... args)
{
// (1.1)
if constexpr (std::is_base_of_v<C, std::decay_t<T>>)
return (t.*f)(std::forward<Args>(args)...);
// (1.2)
else if constexpr (is_reference_wrapper_v<std::decay_t<T>>)
return (t.get().*f)(std::forward<Args>(args)...);
// (1.3)
else
return ((*t).*f)(std::forward<Args>(args)...);
}
template <typename M, typename C, typename T>
decltype(auto) invoke_mop(M C::*f, T&& t)
{
// (1.4)
if constexpr (std::is_base_of_v<C, std::decay_t<T>>)
return t.*f;
// (1.5)
else if constexpr (is_reference_wrapper_v<std::decay_t<T>>)
return t.get().*f;
// (1.6)
else
return (*t).*f;
}
}
template <typename F, typename... Args>
std::invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
noexcept(std::is_nothrow_invocable_v<F, Args...>)
{
// (1.1), (1.2), (1.3)
if constexpr (std::is_member_function_pointer_v<std::remove_reference_t<F>>)
return detail::invoke_mfp(f, std::forward<Args>(args)...);
// (1.4), (1.5), (1.6)
else if constexpr (sizeof...(Args) == 1 &&
std::is_member_object_pointer_v<std::remove_reference_t<F>>)
return detail::invoke_mop(f, std::forward<Args>(args)...);
// (1.7)
else
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template <typename R, typename F, typename... Args>
R invoke_r(F&& f, Args&&... args)
noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>)
{
if constexpr (std::is_void_v<R>)
return static_cast<void>(
invoke(std::forward<F>(f), std::forward<Args>(args)...)
);
else
return invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
}
#endif