Request Short Link
C++ 98
C++ 11
C++ 14
C++ 17
C++ 20
C++ 23
C++ 2c
for-loops as while-loops
array subscription
Show all implicit casts
Show all template parameters of a CallExpr
Use libc++
Transform std::initializer_list
Show noexcept internals
Show padding information
Show coroutine transformation
Show C++ to C transformation
Show object lifetime
Default
15
18
20
22
26
More
GitHub
Patreon
Issues
About
Policies
Examples
C++ Insights @ YouTube
Settings
Version
New C++ Insights Episode
×
Made by
Andreas Fertig
Powered by
Flask
and
CodeMirror
Source:
#include <memory> #include <type_traits> namespace detail { template <typename> struct fn_types; template <typename R, typename... ARG_Ts> struct fn_types<R(ARG_Ts...)> { using type = R(ARG_Ts...); }; template <typename R, typename... ARG_Ts> struct fn_types<R(ARG_Ts...) const> { using type = R(ARG_Ts...); }; template <typename R, typename... ARG_Ts> struct fn_types<R(ARG_Ts...) noexcept> { using type = R(ARG_Ts...); }; template <typename R, typename... ARG_Ts> struct fn_types<R(ARG_Ts...) const noexcept> { using type = R(ARG_Ts...); }; template <typename SIG_T> using fn_types_t = typename fn_types<SIG_T>::type; static_assert(std::is_same_v<fn_types_t<void(int)>, void(int)>); static_assert(std::is_same_v<fn_types_t<int(void) noexcept>, int()>); static_assert(std::is_same_v<fn_types_t<int(bool) const noexcept>, int(bool)>); static_assert(std::is_same_v<fn_types_t<char(int) const throw()>, char(int)>); } // namespace detail namespace detail { template <typename> struct is_nothrow_fn; template <typename R, typename... ARG_Ts> struct is_nothrow_fn<R(ARG_Ts...)> : std::false_type {}; template <typename R, typename... ARG_Ts> struct is_nothrow_fn<R(ARG_Ts...) const> : std::false_type {}; template <typename R, typename... ARG_Ts> struct is_nothrow_fn<R(ARG_Ts...) noexcept> : std::true_type {}; template <typename R, typename... ARG_Ts> struct is_nothrow_fn<R(ARG_Ts...) const noexcept> : std::true_type {}; template <typename SIG_T> inline constexpr bool is_nothrow_fn_v = is_nothrow_fn<SIG_T>::value; static_assert(!is_nothrow_fn_v<int()>); static_assert(!is_nothrow_fn_v<char(int) const>); static_assert(is_nothrow_fn_v<char(int) noexcept>); static_assert(is_nothrow_fn_v<bool() const noexcept>); } // namespace detail namespace detail { template <typename, bool, typename...> struct is_invocable_using; template <typename R, typename... ARG_Ts, typename... T> struct is_invocable_using<R(ARG_Ts...), true, T...> : std::is_nothrow_invocable_r<R, T..., ARG_Ts...> {}; template <typename R, typename... ARG_Ts, typename... T> struct is_invocable_using<R(ARG_Ts...), false, T...> : std::is_invocable_r<R, T..., ARG_Ts...> {}; template <typename SIG_T, typename... T> inline constexpr bool is_invocable_using_v = is_invocable_using<fn_types_t<SIG_T>, is_nothrow_fn_v<SIG_T>, T...>::value; } // namespace detail namespace detail { template <typename> struct is_const_fn; template <typename R, typename... ARG_Ts> struct is_const_fn<R(ARG_Ts...)> : std::false_type {}; template <typename R, typename... ARG_Ts> struct is_const_fn<R(ARG_Ts...) const> : std::true_type {}; template <typename R, typename... ARG_Ts> struct is_const_fn<R(ARG_Ts...) noexcept> : std::false_type {}; template <typename R, typename... ARG_Ts> struct is_const_fn<R(ARG_Ts...) const noexcept> : std::true_type {}; template <typename SIG_T> inline constexpr bool is_const_fn_v = is_const_fn<SIG_T>::value; static_assert(!is_const_fn_v<int()>); static_assert(is_const_fn_v<char(int) const>); static_assert(!is_const_fn_v<char(int) noexcept>); static_assert(is_const_fn_v<bool() const noexcept>); } // namespace detail namespace detail { template <typename T, bool is_const = true> struct cv_fn { using type = const T; }; template <typename T> struct cv_fn<T, false> { using type = T; }; template <typename SIG_T, typename T> using cv_fn_t = typename cv_fn<T, is_const_fn_v<SIG_T>>::type; static_assert(std::is_same_v<cv_fn_t<void(int), int>, int>); static_assert(std::is_same_v<cv_fn_t<int(void) noexcept, char>, char>); static_assert( std::is_same_v<cv_fn_t<int(bool) const noexcept, short>, const short>); static_assert(std::is_same_v<cv_fn_t<char(int) const, bool>, const bool>); }; // namespace detail namespace detail { union bound_entity_type { void* obj_ptr{nullptr}; const void* const_obj_ptr; constexpr bound_entity_type() noexcept = default; template <typename T> requires std::is_object_v<T> constexpr explicit bound_entity_type(T* ptr) noexcept : obj_ptr(ptr) {} template <typename T> requires std::is_object_v<T> constexpr explicit bound_entity_type(const T* ptr) noexcept : const_obj_ptr(ptr) {} }; static_assert(std::is_trivially_copyable_v<bound_entity_type>); template <typename T> constexpr static auto get(bound_entity_type entity) { if constexpr (std::is_const_v<T>) { return static_cast<T*>(entity.const_obj_ptr); } else { return static_cast<T*>(entity.obj_ptr); } } } // namespace detail #include <functional> template <typename SIG_T, typename = detail::fn_types_t<SIG_T>> class function_ref; template <typename SIG_T, typename R, typename... ARG_Ts> class function_ref<SIG_T, R(ARG_Ts...)> { template <typename... T> static constexpr bool is_invocable_using = detail::is_invocable_using_v<SIG_T, T...>; template <typename T> using cv = typename detail::cv_fn_t<SIG_T, T>; static constexpr bool noex = detail::is_nothrow_fn_v<SIG_T>; using bound_entity_t = detail::bound_entity_type; using thunk_ptr_t = R (*)(bound_entity_t, ARG_Ts...) noexcept(noex); public: template <typename F, typename T = std::remove_reference_t<F>> requires(!std::is_same_v<std::remove_cvref_t<F>, function_ref> && !std::is_member_pointer_v<T> && is_invocable_using<cv<T>&>) constexpr function_ref(F&& f) noexcept : m_bound_entity{std::addressof(f)}, m_thunk_ptr{ [](bound_entity_t entity, ARG_Ts... args) noexcept(noex) -> R { cv<T>& obj = *get<T>(entity); if constexpr (std::is_void_v<R>) { std::invoke(obj, std::forward<ARG_Ts>(args)...); } else { return std::invoke(obj, std::forward<ARG_Ts>(args)...); } }} {} R operator()(ARG_Ts... args) const noexcept(noex) { return m_thunk_ptr(m_bound_entity, std::forward<ARG_Ts>(args)...); } private: bound_entity_t m_bound_entity{}; thunk_ptr_t m_thunk_ptr{nullptr}; }; struct functor { int operator()([[maybe_unused]] char) const { return 42; } void operator()([[maybe_unused]] bool) noexcept {} int operator()() const noexcept { return 17; } }; #include <cassert> int main() { { functor fun{}; function_ref<int()> fnref{[]() { return 17; }}; function_ref<int(char) const> cfnref{fun}; function_ref<void(bool) noexcept> exfnref{fun}; function_ref<int() const noexcept> cexfnref{fun}; assert((fnref() == 17)); assert((cfnref('a') == 42)); exfnref(false); assert((cexfnref() == 17)); } { const functor cfun{}; function_ref<int(char) const> cfnref{cfun}; function_ref<int() const noexcept> cexfnref{cfun}; assert((cfnref('a') == 42)); assert((cexfnref() == 17)); } }
Insight:
Console: