/* ***************************************************************************** * kblib is a general utility library for C++14 and C++17, intended to provide * performant high-level abstractions and more expressive ways to do simple * things. * * Copyright (c) 2021 killerbee * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ****************************************************************************/ /** * @file * @brief This header provides some features of C++17 and other * headers for C++14, as well as some other traits. * * @author killerbee * @date 2019-2021 * @copyright GNU General Public Licence v3.0 */ #ifndef KBLIB_FAKESTD_H #define KBLIB_FAKESTD_H #include "tdecl.h" #include #include #include #include #include #if __has_include("gsl/pointers") # include "gsl/pointers" #endif #ifndef KBLIB_FAKESTD # define KBLIB_FAKESTD (__cplusplus < 201703L) #endif namespace KBLIB_NS { template using enable_if_t = typename std::enable_if::type; template using decay_t = typename std::decay::type; template using remove_cvref_t = typename std::remove_reference::type>::type; template using bool_constant = std::integral_constant; #if __cpp_lib_constexpr_functional using std::invoke; #else # if __cpp_lib_apply template constexpr auto invoke(F&& f, Args&&... args) noexcept(noexcept(std::apply( std::forward(f), std::forward_as_tuple(std::forward(args)...)))) -> decltype(auto) { return std::apply(std::forward(f), std::forward_as_tuple(std::forward(args)...)); } # else namespace detail { template >::value, int> = 0> constexpr auto do_invoke(F&& f, Args&&... args) noexcept(noexcept( std::forward(f)(std::forward(args)...))) -> decltype(auto) { return std::forward(f)(std::forward(args)...); } template >::value and std::is_member_function_pointer::value, int> = 0> constexpr auto do_invoke(F f, Object&& obj, Args&&... args) noexcept( noexcept((std::forward(obj).*f)(std::forward(args)...))) -> decltype(auto) { return (obj.*f)(std::forward(args)...); } template ::value and std::is_member_function_pointer::value, int> = 0> constexpr auto do_invoke(F f, Pointer ptr, Args&&... args) noexcept( noexcept((ptr->*f)(std::forward(args)...))) -> decltype(auto) { return (ptr->*f)(std::forward(args)...); } template >::value and std::is_member_object_pointer::value, int> = 0> constexpr auto do_invoke(Member mem, Object&& obj) noexcept -> decltype(auto) { return std::forward(obj).*mem; } template ::value and std::is_member_object_pointer::value, int> = 0> constexpr auto do_invoke(Member mem, Pointer ptr) noexcept -> decltype(auto) { return ptr.*mem; } } // namespace detail template constexpr auto invoke(F&& f, Args&&... args) noexcept(noexcept( detail::do_invoke(std::forward(f), std::forward(args)...))) -> decltype(auto) { return detail::do_invoke(std::forward(f), std::forward(args)...); } # endif // __cpp_lib_apply #endif // __cpp_lib_constexpr_functional /** * @namespace kblib::fakestd * @brief A namespace which holds all the C++14 implementations of C++17 * standard library features. In C++17, it is simply defined as an alias to std. */ #if KBLIB_FAKESTD namespace fakestd { // C++14 implementation of C++17 void_t, invoke_result, // (partially) is_invocable, and is_nothrow_swappable using std::swap; /** * @namespace kblib::fakestd::detail * @brief Implementation details for kblib::fakestd features * * @internal */ namespace detail { template struct invoke_result {}; template struct invoke_result(), std::declval()...))), F, Args...> { using type = decltype(invoke(std::declval(), std::declval()...)); }; } // namespace detail template struct invoke_result : detail::invoke_result {}; template using invoke_result_t = typename invoke_result::type; template struct make_void { typedef void type; }; template using void_t = typename make_void::type; namespace detail { // ALL generic swap overloads MUST already have a declaration available at // this point. struct nat { nat() = delete; nat(const nat&) = delete; nat& operator=(const nat&) = delete; ~nat() = delete; }; struct two { char lx[2]; }; struct is_referenceable_impl { template static Tp& test(int); template static two test(...); }; template struct is_referenceable : std::integral_constant< bool, not std::is_same(0)), two>::value> {}; template ::value and not std::is_void::value> struct swappable_with { template static decltype(swap(std::declval(), std::declval())) test_swap(int); template static nat test_swap(long); // Extra parens are needed for the C++03 definition of decltype. using swap1 = decltype((test_swap(0))); using swap2 = decltype((test_swap(0))); static const bool value = not std::is_same::value and not std::is_same::value; }; template struct swappable_with : std::false_type {}; template ::value> struct nothrow_swappable_with { static const bool value = noexcept( swap(std::declval(), std::declval()))and noexcept(swap(std::declval(), std::declval())); }; template struct nothrow_swappable_with : std::false_type {}; } // namespace detail template struct is_swappable : public std::integral_constant::value> {}; template struct is_nothrow_swappable : public std::integral_constant< bool, detail::nothrow_swappable_with::value> {}; # if KBLIB_USE_CXX17 template struct is_swappable_with : public std::integral_constant::value> { }; template struct is_swappable : public std::conditional< detail::is_referenceable::value, is_swappable_with::type, typename std::add_lvalue_reference::type>, std::false_type>::type {}; template struct is_nothrow_swappable_with : public integral_constant< bool, detail::nothrow_swappable_with::value> {}; template struct is_nothrow_swappable : public conditional< detail::is_referenceable::value, is_nothrow_swappable_with::type, typename add_lvalue_reference::type>, false_type>::type {}; template constexpr bool is_swappable_with_v = is_swappable_with::value; template constexpr bool is_swappable_v = is_swappable::value; template constexpr bool is_nothrow_swappable_with_v = is_nothrow_swappable_with::value; template constexpr bool is_nothrow_swappable_v = is_nothrow_swappable::value; # endif namespace detail { template struct not_fn_t { constexpr explicit not_fn_t(F&& f) : fd(std::forward(f)) {} constexpr not_fn_t(const not_fn_t&) = default; constexpr not_fn_t(not_fn_t&&) = default; template constexpr auto operator()(Args&&... args) & -> decltype( not std::declval&, Args...>>()) { return not invoke(fd, std::forward(args)...); } template constexpr auto operator()(Args&&... args) const& -> decltype( not std::declval< invoke_result_t const&, Args...>>()) { return not invoke(std::move(fd), std::forward(args)...); } std::decay_t fd; }; } // namespace detail template auto not_fn(F&& f) -> detail::not_fn_t { return detail::not_fn_t(std::forward(f)); } struct in_place_t { explicit in_place_t() = default; }; static constexpr in_place_t in_place{}; template constexpr auto max_element(ForwardIt first, ForwardIt last) -> ForwardIt { if (first == last) return last; ForwardIt largest = first; ++first; for (; first != last; ++first) { if (*largest < *first) { largest = first; } } return largest; } template constexpr auto max_element(ForwardIt first, ForwardIt last, Compare comp) -> ForwardIt { if (first == last) return last; ForwardIt largest = first; ++first; for (; first != last; ++first) { if (comp(*largest, *first)) { largest = first; } } return largest; } template constexpr auto size(const C& c) -> decltype(c.size()) { return c.size(); } template constexpr auto size(const T (&)[N]) noexcept -> std::size_t { return N; } // Adapted from libstdc++ code, licensed under GPL namespace detail { // invokable template struct invokable_r { template static auto try_call(int) -> decltype(kblib::invoke(std::declval(), std::declval()...)); template static auto try_call(...) -> detail::nat; using Result = decltype(try_call(0)); using type = typename std::conditional< not std::is_same::value, typename std::conditional::value, std::true_type, std::is_convertible>::type, std::false_type>::type; static const bool value = type::value; }; template using invokable = invokable_r; template struct nothrow_invokable_r_imp { static const bool value = false; }; template struct nothrow_invokable_r_imp { using ThisT = nothrow_invokable_r_imp; template static void test_noexcept(Tp) noexcept; static const bool value = noexcept(ThisT::test_noexcept( kblib::invoke(std::declval(), std::declval()...))); }; template struct nothrow_invokable_r_imp { static const bool value = noexcept( kblib::invoke(std::declval(), std::declval()...)); }; template using nothrow_invokable_r = nothrow_invokable_r_imp::value, std::is_void::value, Ret, Fp, Args...>; template using nothrow_invokable = nothrow_invokable_r_imp::value, true, void, Fp, Args...>; template struct invoke_of : public std::enable_if< invokable::value, typename invokable_r::Result> {}; } // namespace detail // is_invocable template struct is_invocable : std::integral_constant::value> {}; template struct is_invocable_r : std::integral_constant::value> { }; template constexpr bool is_invocable_v = is_invocable::value; template constexpr bool is_invocable_r_v = is_invocable_r::value; // is_nothrow_invocable template struct is_nothrow_invocable : std::integral_constant::value> { }; template struct is_nothrow_invocable_r : std::integral_constant< bool, detail::nothrow_invokable_r::value> {}; template constexpr bool is_nothrow_invocable_v = is_nothrow_invocable::value; template constexpr bool is_nothrow_invocable_r_v = is_nothrow_invocable_r::value; } // namespace fakestd #else namespace fakestd = std; #endif using fakestd::is_invocable; using fakestd::is_invocable_v; using fakestd::is_invocable_r; using fakestd::is_invocable_r_v; using fakestd::is_nothrow_invocable; using fakestd::is_nothrow_invocable_v; using fakestd::is_nothrow_invocable_r; using fakestd::is_nothrow_invocable_r_v; template struct meta_type {}; template struct meta_type { using type = T; }; template using meta_type_t = typename meta_type::type; template struct void_if {}; template <> struct void_if : meta_type {}; template using void_if_t = typename void_if::type; using fakestd::void_t; // metafunction_success: // SFINAE detector for a ::type member type template struct metafunction_success : std::false_type {}; template struct metafunction_success> : std::true_type {}; template struct is_callable : metafunction_success> {}; template using metafunction_value_t = std::integral_constant; /** * @brief Essentially just like std::enable_if, but with a different name that * makes it clearer what it does in the context of return type SFINAE. */ template struct return_assert {}; template struct return_assert : meta_type {}; template using return_assert_t = typename return_assert::type; namespace detail { template struct apply_impl { template constexpr static auto do_apply(F&& f, Arg&& arg) noexcept( noexcept(kblib::invoke(std::forward(f), std::get(std::forward(arg))...))) -> decltype(auto) { return kblib::invoke(std::forward(f), std::get(std::forward(arg))...); } }; } // namespace detail template constexpr auto apply(F&& f, Arg&& arg) noexcept( noexcept(detail::apply_impl::do_apply( std::forward(f), std::forward(arg), std::index_sequence::value>{}))) -> decltype(auto) { return detail::apply_impl::do_apply( std::forward(f), std::forward(arg), std::index_sequence::value>{}); } template KBLIB_NODISCARD auto to_unique(gsl::owner p) -> std::unique_ptr { return std::unique_ptr(p); } template KBLIB_NODISCARD auto to_unique(gsl::owner p, D&& d) -> std::unique_ptr { return std::unique_ptr(p, d); } /** * @brief Cast integral argument to corresponding unsigned type */ template KBLIB_NODISCARD constexpr auto to_unsigned(I x) -> std::make_unsigned_t { return static_cast>(x); } /** * @brief Cast integral argument to corresponding signed type */ template KBLIB_NODISCARD constexpr auto to_signed(I x) -> std::make_signed_t { return static_cast>(x); } /** * @brief Cast argument to equivalently-sized type with the same signednessas * the template parameter */ template KBLIB_NODISCARD constexpr auto signed_cast(F x) -> enable_if_t::value and std::is_integral::value and std::is_signed::value, std::make_signed_t> { return to_signed(x); } /** * @brief Cast argument to equivalently-sized type with the same signednessas * the template parameter */ template KBLIB_NODISCARD constexpr auto signed_cast(F x) -> enable_if_t::value and std::is_integral::value and std::is_unsigned::value, std::make_unsigned_t> { return to_unsigned(x); } template struct has_member_swap { private: using yes = char (&)[1]; using no = char (&)[2]; template static auto check(decltype(&C::swap)) -> yes; template static auto check(...) -> no; public: constexpr static bool value = sizeof(check(nullptr)) == sizeof(yes); }; template struct is_tuple_like : std::false_type {}; template struct is_tuple_like::type>> : std::true_type {}; namespace detail { template constexpr auto ignore(Ts&&... /*unused*/) noexcept -> void {} template constexpr auto swap_tuple_impl(T& a, T& b, std::index_sequence /*unused*/) noexcept( noexcept(ignore(((void)swap(std::get(a), std::get(b)), 0)...))) -> void { ignore(((void)swap(std::get(a), std::get(b)), 0)...); } } // namespace detail KBLIB_UNUSED struct { /** * @brief Swaps two objects, using move operations. * * @param a,b The objects that will be swapped. */ template ::value and not is_tuple_like::value, int> = 0> KBLIB_UNUSED constexpr auto operator()(T& a, T& b) const noexcept(std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value) -> void { auto tmp = std::move(a); a = std::move(b); b = std::move(tmp); return; } /** * @brief Swaps two objects, using a member swap function, if detected. * * @param a,b The objects that will be swapped. */ template ::value, int> = 0> KBLIB_UNUSED constexpr auto operator()(T& a, T& b) const noexcept(noexcept(a.swap(b))) -> void { a.swap(b); return; } /** * @brief Swaps two arrays elementwise. * * @param a,b The arrays that will be swapped. */ template KBLIB_UNUSED constexpr auto operator()(T (&a)[N], T (&b)[N]) const noexcept(std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value) -> void { for (std::size_t i = 0; i < N; ++i) { swap(a[i], b[i]); } } /** * @brief Swaps two tuples elementwise. * * @param a,b The tuples that will be swapped. */ template ::value and not has_member_swap::value, std::size_t> N = std::tuple_size::value> KBLIB_UNUSED constexpr auto operator()(T& a, T& b) const noexcept(noexcept( detail::swap_tuple_impl(a, b, std::make_index_sequence{}))) -> void { detail::swap_tuple_impl(a, b, std::make_index_sequence{}); } } KBLIB_CONSTANT swap; template KBLIB_NODISCARD constexpr auto exchange(T& obj, U&& new_value) -> T { T old_value = std::move(obj); obj = std::forward(new_value); return old_value; } #if KBLIB_USE_CXX17 namespace detail { template constexpr std::intmax_t max_val = std::numeric_limits::max(); KBLIB_NODISCARD constexpr auto msb(std::uintmax_t x) -> std::uintmax_t { x |= (x >> 1u); x |= (x >> 2u); x |= (x >> 4u); x |= (x >> 8u); x |= (x >> 16u); x |= (x >> 32u); return (x & ~(x >> 1u)); } template KBLIB_NODISCARD constexpr auto msb_possible() -> Num { return static_cast(typename std::make_unsigned::type{1} << (std::numeric_limits::digits - 1u)); } template struct type_list { template using type = typename std::tuple_element>::type; }; template struct type_map_el { constexpr static auto key = K; using value = V; }; template struct type_map { using types = type_list; template using element = typename types::template type; template KBLIB_NODISCARD constexpr static auto get() noexcept -> auto { static_assert(I < sizeof...(Vals), "key not found"); if constexpr (Comp{}(key, element::key)) { return tag::value>{}; } else { return get(); } } template KBLIB_NODISCARD constexpr static auto get_default() noexcept -> auto { if constexpr (I == sizeof...(Vals)) { return Default(); } else if constexpr (Comp{}(key, element::key)) { return tag::value>{}; } else { return get(); } } }; template using make_smap_el = type_map_el(msb_possible()), N>; template struct next_larger_signed { static_assert(max_val < max_val, "Cannot safely promote intmax_t."); struct false_compare { template constexpr auto operator()(U, U) noexcept -> bool { return true; } }; using ints_map = type_map< std::intmax_t, std::less<>, make_smap_el, make_smap_el, make_smap_el, make_smap_el, make_smap_el>; using type = typename decltype( ints_map::template get_default + 1>())::type; }; template ::value> struct filter_signed; template struct filter_signed { using type = N; }; template using filter_signed_t = typename filter_signed::type; template ::value> struct filter_unsigned; template struct filter_unsigned { using type = N; }; template using filter_unsigned_t = typename filter_unsigned::type; } // namespace detail template struct safe_signed; template struct safe_signed::value, void>> { using type = std::conditional_t< std::is_signed::value, N, typename detail::next_larger_signed>::type>; }; template using safe_signed_t = typename safe_signed::type; template KBLIB_NODISCARD constexpr auto signed_promote(N x) noexcept -> safe_signed_t { return static_cast>(x); } #endif template ::type>::value> struct copy_const : meta_type {}; template struct copy_const : meta_type {}; template struct copy_const : meta_type {}; template struct copy_const : meta_type {}; template using copy_const_t = typename copy_const::type; template struct value_detected : std::false_type {}; template struct value_detected> : std::true_type { using type = typename T::value_type; }; template constexpr bool value_detected_v = value_detected::value; template using value_detected_t = typename value_detected::type; template struct key_detected : std::false_type {}; template struct key_detected> : std::true_type { using type = typename T::key_type; }; template constexpr bool key_detected_v = key_detected::value; template using key_detected_t = typename key_detected::type; template struct mapped_detected : std::false_type {}; template struct mapped_detected> : std::true_type { using type = typename T::mapped_type; }; template constexpr bool mapped_detected_v = mapped_detected::value; template using mapped_detected_t = typename mapped_detected::type; template struct hash_detected : std::false_type {}; template struct hash_detected> : std::true_type { using type = typename T::hasher; }; template constexpr bool hash_detected_v = hash_detected::value; template using hash_detected_t = typename hash_detected::type; template , typename T = typename Container::value_type> struct value_type_linear {}; template struct value_type_linear : meta_type {}; template using value_type_linear_t = typename value_type_linear::type; template constexpr static bool is_linear_container_v = value_detected_v and not key_detected_v; template struct is_linear_container : bool_constant> {}; template , bool = mapped_detected_v> struct key_type_setlike {}; template struct key_type_setlike : meta_type {}; template using key_type_setlike_t = typename key_type_setlike::type; template constexpr static bool is_setlike_v = (key_detected_v< Container> and value_detected_v and not mapped_detected_v and std::is_same, value_detected_t>::value); template KBLIB_NODISCARD constexpr auto equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) -> bool { for (; first1 != last1; ++first1, ++first2) { if (not (*first1 == *first2)) { return false; } } return true; } template ::value, int> = 0> KBLIB_NODISCARD constexpr auto equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryPredicate p) -> bool { for (; first1 != last1; ++first1, ++first2) { if (not p(*first1, *first2)) { return false; } } return true; } template ::iterator_category>::value and std::is_base_of::iterator_category>::value, int> = 0> KBLIB_NODISCARD constexpr auto equal(RandomIt1 first1, RandomIt1 last1, RandomIt2 first2, RandomIt2 last2) -> bool { if (std::distance(first1, last1) == std::distance(first2, last2)) { return false; } for (; first1 != last1; ++first1, ++first2) { if (not (*first1 == *first2)) { return false; } } return true; } template ::iterator_category>::value and std::is_base_of::iterator_category>::value, int> = 0> KBLIB_NODISCARD constexpr auto equal(RandomIt1 first1, RandomIt1 last1, RandomIt2 first2, RandomIt2 last2, BinaryPredicate p) -> bool { if (std::distance(first1, last1) == std::distance(first2, last2)) { return false; } for (; first1 != last1; ++first1, ++first2) { if (not p(*first1, *first2)) { return false; } } return true; } template < class InputIt1, class InputIt2, typename kblib::enable_if_t< not std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits::iterator_category>::value or not std::is_base_of::iterator_category>::value, int> = 0> KBLIB_NODISCARD constexpr auto equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) -> bool { for (; first1 != last1 and first2 != last2; ++first1, ++first2) { if (not (*first1 == *first2)) { return false; } } return (first1 == last1 and first2 == last2); } template < typename InputIt1, typename InputIt2, typename BinaryPredicate, typename kblib::enable_if_t< not std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits::iterator_category>::value or not std::is_base_of::iterator_category>::value, int> = 0> KBLIB_NODISCARD constexpr auto equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p) -> bool { for (; first1 != last1 and first2 != last2; ++first1, ++first2) { if (not p(*first1, *first2)) { return false; } } return (first1 == last1 and first2 == last2); } template KBLIB_NODISCARD constexpr auto size(const C& c) -> decltype(c.size()) { return c.size(); } template KBLIB_NODISCARD constexpr auto size(const T (&)[N]) noexcept -> std::size_t { return N; } template KBLIB_NODISCARD constexpr auto lexicographical_compare(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) -> bool { for (; (first1 != last1) and (first2 != last2); ++first1, (void)++first2) { if (*first1 < *first2) return true; if (*first2 < *first1) return false; } return (first1 == last1) and (first2 != last2); } namespace detail { template struct pointer { using type = T*; }; template struct pointer> { using type = typename D::pointer; }; } // namespace detail constexpr struct in_place_agg_t { } in_place_agg; template class heap_value { public: using element_type = T; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; constexpr heap_value() noexcept : p{nullptr} {} constexpr heap_value(std::nullptr_t) noexcept : p{nullptr} {} template ::value> = 0> heap_value(fakestd::in_place_t, Args&&... args) : p{new T(args...)} {} template heap_value(in_place_agg_t, Args&&... args) : p{new T{args...}} {} heap_value(const heap_value& u) : p{(u.p ? (new T(*u.p)) : nullptr)} {} heap_value(heap_value&& u) noexcept : p{std::exchange(u.p, nullptr)} {} auto operator=(const heap_value& u) & -> heap_value& { if (this == &u) { return *this; } else if (not u) { p.reset(); } else if (p) { *p = *u; } else { p.reset(new T(*u.p)); } return *this; } auto operator=(heap_value&& u) & noexcept -> heap_value& { if (this == &u) { return *this; } p = std::exchange(u.p, nullptr); return *this; } auto operator=(const T& val) & -> heap_value& { if (this == &val) { return *this; } p.reset(new T(val)); } auto operator=(T&& val) & -> heap_value& { if (this == &val) { return *this; } p.reset(new T(std::move(val))); } auto assign() & -> void { p.reset(new T()); } auto assign(const T& val) & -> void { p.reset(new T(val)); } auto assign(T&& val) & -> void { p.reset(new T(std::move(val))); } template ::value> = 0> auto assign(fakestd::in_place_t, Args&&... args) -> void { p.reset(new T(std::forward(args)...)); } template auto assign(in_place_agg_t, Args&&... args) -> void { p.reset(new T{std::forward(args)...}); } auto reset() noexcept -> void { p.reset(); return; } KBLIB_NODISCARD explicit operator bool() const& noexcept { return p != nullptr; } constexpr auto swap(heap_value& other) noexcept -> void { kblib::swap(p, other.p); } KBLIB_NODISCARD auto get() & noexcept -> pointer { return p.get(); } KBLIB_NODISCARD auto get() const& noexcept -> const_pointer { return p.get(); } KBLIB_NODISCARD auto value() & noexcept -> reference { return *p; } KBLIB_NODISCARD auto value() const& noexcept -> const_reference { return *p; } KBLIB_NODISCARD auto value() && noexcept -> T&& { return *p; } KBLIB_NODISCARD auto value() const&& noexcept -> const T&& { return *p; } KBLIB_NODISCARD auto operator*() & noexcept -> reference { return *p; } KBLIB_NODISCARD auto operator*() const& noexcept -> const_reference { return *p; } KBLIB_NODISCARD auto operator*() && noexcept -> T&& { return *p; } KBLIB_NODISCARD auto operator*() const&& noexcept -> const T&& { return *p; } KBLIB_NODISCARD auto operator->() & noexcept -> pointer { return p.get(); } KBLIB_NODISCARD auto operator->() const& noexcept -> const_pointer { return p.get(); } ~heap_value() = default; private: std::unique_ptr p; }; template class heap_value2 : private std::unique_ptr { using Base = std::unique_ptr; public: using typename Base::deleter_type; using typename Base::element_type; using typename Base::pointer; using reference = decltype(*std::declval()); using const_reference = const element_type&; using Base::Base; using Base::operator=; heap_value2(const heap_value2& other); heap_value2& operator=(const heap_value2& other); using Base::release; using Base::reset; using Base::swap; using Base::get; using Base::get_deleter; using Base::operator bool; using Base::operator*; using Base::operator->; KBLIB_NODISCARD auto value() & noexcept -> reference { return **this; } KBLIB_NODISCARD auto value() const& noexcept -> const_reference { return **this; } KBLIB_NODISCARD auto value() && noexcept -> element_type&& { return **this; } KBLIB_NODISCARD auto value() const&& noexcept -> const element_type&& { return **this; } ~heap_value2() = default; }; template class heap_value2 : private std::unique_ptr { using Base = std::unique_ptr; public: using typename Base::deleter_type; using typename Base::element_type; using typename Base::pointer; using reference = element_type&; using const_reference = const element_type&; using Base::Base; using Base::operator=; heap_value2(const heap_value2& other); heap_value2& operator=(const heap_value2& other); using Base::release; using Base::reset; using Base::swap; using Base::get; using Base::get_deleter; using Base::operator bool; using Base::operator[]; KBLIB_NODISCARD auto value() & noexcept -> reference { return **this; } KBLIB_NODISCARD auto value() const& noexcept -> const_reference { return **this; } KBLIB_NODISCARD auto value() && noexcept -> element_type&& { return **this; } KBLIB_NODISCARD auto value() const&& noexcept -> const element_type&& { return **this; } ~heap_value2() = default; }; } // namespace KBLIB_NS #endif // KBLIB_FAKESTD_H