/* * Copyright (c) 2018 Jaroslav Jindrak * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LIBCPP_FUNCTIONAL #define LIBCPP_FUNCTIONAL #include #include #include #include #include namespace std { namespace aux { /** * 20.9.2, requirements: */ template decltype(auto) invoke(R T::* f, T1&& t1, Ts&&... args) { if constexpr (is_member_function_pointer_v) { if constexpr (is_base_of_v>) // (1.1) return (t1.*f)(forward(args)...); else // (1.2) return ((*t1).*f)(forward(args)...); } else if constexpr (is_member_object_pointer_v && sizeof...(args) == 0) { /** * Note: Standard requires to N be equal to 1, but we take t1 directly * so we need sizeof...(args) to be 0. */ if constexpr (is_base_of_v>) // (1.3) return t1.*f; else // (1.4) return (*t1).*f; } /** * Note: If this condition holds this will not be reachable, * but a new addition to the standard (17.7 point (8.1)) * prohibits us from simply using false as the condition here, * so we use this because we know it is false here. */ static_assert(is_member_function_pointer_v, "invalid invoke"); } template decltype(auto) invoke(F&& f, Args&&... args) { // (1.5) return f(forward(args)...); } } /** * 20.9.3, invoke: */ template result_of_t invoke(F&& f, Args&&... args) { return aux::invoke(forward(f)(forward(args)...)); } /** * 20.9.4, reference_wrapper: */ template class reference_wrapper { public: using type = T; // TODO: conditional typedefs reference_wrapper(type& val) noexcept : data_{&val} { /* DUMMY BODY */ } reference_wrapper(type&&) = delete; reference_wrapper(const reference_wrapper& other) noexcept : data_{other.data_} { /* DUMMY BODY */ } reference_wrapper& operator=(const reference_wrapper& other) noexcept { data_ = other.data_; return *this; } operator type&() const noexcept { return *data_; } type& get() const noexcept { return *data_; } template result_of_t operator()(Args&&... args) const { return invoke(*data_, std::forward(args)...); } private: type* data_; }; template reference_wrapper ref(T& t) noexcept { return reference_wrapper{t}; } template reference_wrapper cref(const T& t) noexcept { return reference_wrapper{t}; } template void ref(const T&&) = delete; template void cref(const T&&) = delete; template reference_wrapper ref(reference_wrapper t) noexcept { return ref(t.get()); } template reference_wrapper cref(reference_wrapper t) noexcept { return cref(t.get()); } /** * 20.9.5, arithmetic operations: */ template struct plus { constexpr T operator()(const T& lhs, const T& rhs) const { return lhs + rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = T; }; template struct minus { constexpr T operator()(const T& lhs, const T& rhs) const { return lhs - rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = T; }; template struct multiplies { constexpr T operator()(const T& lhs, const T& rhs) const { return lhs * rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = T; }; template struct divides { constexpr T operator()(const T& lhs, const T& rhs) const { return lhs / rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = T; }; template struct modulus { constexpr T operator()(const T& lhs, const T& rhs) const { return lhs % rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = T; }; template struct negate { constexpr T operator()(const T& x) const { return -x; } using argument_type = T; using result_type = T; }; namespace aux { /** * Used by some functions like std::set::find to determine * whether a functor is transparent. */ struct transparent_t; template struct is_transparent: false_type { /* DUMMY BODY */ }; template struct is_transparent> : true_type { /* DUMMY BODY */ }; template inline constexpr bool is_transparent_v = is_transparent::value; } template<> struct plus { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) + forward(rhs)) { return forward(lhs) + forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct minus { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) - forward(rhs)) { return forward(lhs) - forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct multiplies { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) * forward(rhs)) { return forward(lhs) * forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct divides { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) / forward(rhs)) { return forward(lhs) / forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct modulus { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) % forward(rhs)) { return forward(lhs) % forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct negate { template constexpr auto operator()(T&& x) const -> decltype(-forward(x)) { return -forward(x); } using is_transparent = aux::transparent_t; }; /** * 20.9.6, comparisons: */ template struct equal_to { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = bool; }; template struct not_equal_to { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs != rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = bool; }; template struct greater { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs > rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = bool; }; template struct less { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = bool; }; template struct greater_equal { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs >= rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = bool; }; template struct less_equal { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs <= rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = bool; }; template<> struct equal_to { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) == forward(rhs)) { return forward(lhs) == forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct not_equal_to { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) != forward(rhs)) { return forward(lhs) != forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct greater { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) > forward(rhs)) { return forward(lhs) > forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct less { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) < forward(rhs)) { return forward(lhs) < forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct greater_equal { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) >= forward(rhs)) { return forward(lhs) >= forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct less_equal { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) <= forward(rhs)) { return forward(lhs) <= forward(rhs); } using is_transparent = aux::transparent_t; }; /** * 20.9.7, logical operations: */ template struct logical_and { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs && rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = bool; }; template struct logical_or { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs || rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = bool; }; template struct logical_not { constexpr bool operator()(const T& x) const { return !x; } using argument_type = T; using result_type = bool; }; template<> struct logical_and { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) && forward(rhs)) { return forward(lhs) && forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct logical_or { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) || forward(rhs)) { return forward(lhs) || forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct logical_not { template constexpr auto operator()(T&& x) const -> decltype(!forward(x)) { return !forward(x); } using is_transparent = aux::transparent_t; }; /** * 20.9.8, bitwise operations: */ template struct bit_and { constexpr T operator()(const T& lhs, const T& rhs) const { return lhs & rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = T; }; template struct bit_or { constexpr T operator()(const T& lhs, const T& rhs) const { return lhs | rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = T; }; template struct bit_xor { constexpr T operator()(const T& lhs, const T& rhs) const { return lhs ^ rhs; } using first_argument_type = T; using second_argument_type = T; using result_type = T; }; template struct bit_not { constexpr bool operator()(const T& x) const { return ~x; } using argument_type = T; using result_type = T; }; template<> struct bit_and { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) & forward(rhs)) { return forward(lhs) & forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct bit_or { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) | forward(rhs)) { return forward(lhs) | forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct bit_xor { template constexpr auto operator()(T&& lhs, U&& rhs) const -> decltype(forward(lhs) ^ forward(rhs)) { return forward(lhs) ^ forward(rhs); } using is_transparent = aux::transparent_t; }; template<> struct bit_not { template constexpr auto operator()(T&& x) const -> decltype(~forward(x)) { return ~forward(x); } using is_transparent = aux::transparent_t; }; /** * 20.9.9, negators: */ template class unary_negate { public: using result_type = bool; using argument_type = typename Predicate::argument_type; constexpr explicit unary_negate(const Predicate& pred) : pred_{pred} { /* DUMMY BODY */ } constexpr result_type operator()(const argument_type& arg) { return !pred_(arg); } private: Predicate pred_; }; template constexpr unary_negate not1(const Predicate& pred) { return unary_negate{pred}; } template class binary_negate { public: using result_type = bool; using first_argument_type = typename Predicate::first_argument_type; using second_argument_type = typename Predicate::second_argument_type; constexpr explicit binary_negate(const Predicate& pred) : pred_{pred} { /* DUMMY BODY */ } constexpr result_type operator()(const first_argument_type& arg1, const second_argument_type& arg2) { return !pred_(arg1, arg2); } private: Predicate pred_; }; template constexpr binary_negate not2(const Predicate& pred); /** * 20.9.12, polymorphic function adaptors: */ namespace aux { // TODO: fix this /* template */ /* struct is_callable_impl: false_type */ /* { /1* DUMMY BODY *1/ }; */ /* template */ /* struct is_callable_impl< */ /* void_t(), declval()..., R))>, */ /* R, Args... */ /* > : true_type */ /* { /1* DUMMY BODY *1/ }; */ /* template */ /* struct is_callable: is_callable_impl, T> */ /* { /1* DUMMY BODY *1/ }; */ template R invoke_callable(Callable* clbl, Args&&... args) { return (*clbl)(forward(args)...); } template void copy_callable(Callable* to, Callable* from) { new(to) Callable{*from}; } template void destroy_callable(Callable* clbl) { if (clbl) clbl->~Callable(); } } // TODO: implement class bad_function_call; template class function; // undefined /** * Note: Ideally, this implementation wouldn't * copy the target if it was a pointer to * a function, but for the simplicity of the * implementation, we do copy even in that * case for now. It would be a nice optimization * if this was changed in the future. */ template class function { public: using result_type = R; // TODO: conditional typedefs /** * 20.9.12.2.1, construct/copy/destroy: */ function() noexcept : callable_{}, callable_size_{}, call_{}, copy_{}, dest_{} { /* DUMMY BODY */ } function(nullptr_t) noexcept : function{} { /* DUMMY BODY */ } function(const function& other) : callable_{}, callable_size_{other.callable_size_}, call_{other.call_}, copy_{other.copy_}, dest_{other.dest_} { callable_ = new uint8_t[callable_size_]; (*copy_)(callable_, other.callable_); } function(function&& other) : callable_{other.callable_}, callable_size_{other.callable_size_}, call_{other.call_}, copy_{other.copy_}, dest_{other.dest_} { other.callable_ = nullptr; other.callable_size_ = size_t{}; other.call_ = nullptr; other.copy_ = nullptr; other.dest_ = nullptr; } // TODO: shall not participate in overloading unless aux::is_callable template function(F f) : callable_{}, callable_size_{sizeof(F)}, call_{(call_t)aux::invoke_callable}, copy_{(copy_t)aux::copy_callable}, dest_{(dest_t)aux::destroy_callable} { callable_ = new uint8_t[callable_size_]; (*copy_)(callable_, (uint8_t*)&f); } /** * Note: For the moment we're ignoring the allocator * for simplicity of the implementation. */ template function(allocator_arg_t, const A& a) noexcept : function{} { /* DUMMY BODY */ } template function(allocator_arg_t, const A& a, nullptr_t) noexcept : function{} { /* DUMMY BODY */ } template function(allocator_arg_t, const A& a, const function& other) : function{other} { /* DUMMY BODY */ } template function(allocator_arg_t, const A& a, function&& other) : function{move(other)} { /* DUMMY BODY */ } // TODO: shall not participate in overloading unless aux::is_callable template function(allocator_arg_t, const A& a, F f) : function{f} { /* DUMMY BODY */ } function& operator=(const function& rhs) { function{rhs}.swap(*this); return *this; } /** * Note: We have to copy call_, copy_ * and dest_ because they can be templated * by a type F we don't know. */ function& operator=(function&& rhs) { clear_(); callable_ = rhs.callable_; callable_size_ = rhs.callable_size_; call_ = rhs.call_; copy_ = rhs.copy_; dest_ = rhs.dest_; rhs.callable_ = nullptr; rhs.callable_size_ = size_t{}; rhs.call_ = nullptr; rhs.copy_ = nullptr; rhs.dest_ = nullptr; return *this; } function& operator=(nullptr_t) noexcept { clear_(); return *this; } // TODO: shall not participate in overloading unless aux::is_callable template function& operator=(F&& f) { callable_size_ = sizeof(F); callable_ = new uint8_t[callable_size_]; call_ = aux::invoke_callable; copy_ = aux::copy_callable; dest_ = aux::destroy_callable; (*copy_)(callable_, (uint8_t*)&f); } template function& operator=(reference_wrapper ref) noexcept { return (*this) = ref.get(); } ~function() { if (callable_) { (*dest_)(callable_); delete[] callable_; } } /** * 20.9.12.2.2, function modifiers: */ void swap(function& other) noexcept { std::swap(callable_, other.callable_); std::swap(callable_size_, other.callable_size_); std::swap(call_, other.call_); std::swap(copy_, other.copy_); std::swap(dest_, other.dest_); } template void assign(F&& f, const A& a) { function{allocator_arg, a, forward(f)}.swap(*this); } /** * 20.9.12.2.3, function capacity: */ explicit operator bool() const noexcept { return callable_ != nullptr; } /** * 20.9.12.2.4, function invocation: */ result_type operator()(Args... args) const { // TODO: throw bad_function_call if !callable_ || !call_ if constexpr (is_same_v) (*call_)(callable_, forward(args)...); else return (*call_)(callable_, forward(args)...); } /** * 20.9.12.2.5, function target access: */ const type_info& target_type() const noexcept { return typeid(*callable_); } template T* target() noexcept { if (target_type() == typeid(T)) return (T*)callable_; else return nullptr; } template const T* target() const noexcept { if (target_type() == typeid(T)) return (T*)callable_; else return nullptr; } private: using call_t = R(*)(uint8_t*, Args&&...); using copy_t = void (*)(uint8_t*, uint8_t*); using dest_t = void (*)(uint8_t*); uint8_t* callable_; size_t callable_size_; call_t call_; copy_t copy_; dest_t dest_; void clear_() { if (callable_) { (*dest_)(callable_); delete[] callable_; callable_ = nullptr; } } }; /** * 20.9.12.2.6, null pointer comparisons: */ template bool operator==(const function& f, nullptr_t) noexcept { return !f; } template bool operator==(nullptr_t, const function& f) noexcept { return !f; } template bool operator!=(const function& f, nullptr_t) noexcept { return (bool)f; } template bool operator!=(nullptr_t, const function& f) noexcept { return (bool)f; } /** * 20.9.12.2.7, specialized algorithms: */ template void swap(function& f1, function& f2) { f1.swap(f2); } template struct uses_allocator, Alloc> : true_type { /* DUMMY BODY */ }; /** * 20.9.10, bind: */ namespace aux { template struct placeholder_t { constexpr placeholder_t() = default; }; } template struct is_placeholder: integral_constant { /* DUMMY BODY */ }; template // Note: const because they are all constexpr. struct is_placeholder> : integral_constant { /* DUMMY BODY */ }; template inline constexpr int is_placeholder_v = is_placeholder::value; namespace aux { template struct bind_arg_index { /* DUMMY BODY */ }; template class bind_bound_args { public: template constexpr bind_bound_args(BoundArgs&&... args) : tpl_{forward(args)...} { /* DUMMY BODY */ } template constexpr decltype(auto) operator[](bind_arg_index) { return get(tpl_); } private: tuple tpl_; }; template class bind_arg_filter { public: bind_arg_filter(Args&&... args) : args_{forward(args)...} { /* DUMMY BODY */ } // TODO: enable if T != ref_wrapper template constexpr decltype(auto) operator[](T&& t) { // Since placeholders are constexpr, this is a worse match for them. return forward(t); } template constexpr decltype(auto) operator[](const placeholder_t) { /** * Come on, it's int! Why not use -1 as not placeholder * and start them at 0? -.- */ /* return get> - 1>(args_); */ return get(args_); } // TODO: overload the operator for reference_wrapper private: tuple args_; }; template class bind_t { // TODO: conditional typedefs public: template constexpr bind_t(F&& f, BoundArgs&&... args) : func_{forward(f)}, bound_args_{forward(args)...} { /* DUMMY BODY */ } template constexpr decltype(auto) operator()(ActualArgs&&... args) { return invoke_( make_index_sequence{}, forward(args)... ); } private: function> func_; bind_bound_args bound_args_; template constexpr decltype(auto) invoke_( index_sequence, ActualArgs&&... args ) { bind_arg_filter filter{forward(args)...}; return invoke( func_, filter[bound_args_[bind_arg_index()]]... ); } }; } template struct is_bind_expression: false_type { /* DUMMY BODY */ }; template struct is_bind_expression> : true_type { /* DUMMY BODY */ }; template aux::bind_t bind(F&& f, Args&&... args) { return aux::bind_t{forward(f), forward(args)...}; } template aux::bind_t bind(F&& f, Args&&... args); namespace placeholders { /** * Note: The number of placeholders is * implementation defined, we've chosen * 8 because it is a nice number * and should be enough for any function * call. * Note: According to the C++14 standard, these * are all extern non-const. We decided to use * the C++17 form of them being inline constexpr * because it is more convenient, makes sense * and would eventually need to be upgraded * anyway. */ inline constexpr aux::placeholder_t<1> _1; inline constexpr aux::placeholder_t<2> _2; inline constexpr aux::placeholder_t<3> _3; inline constexpr aux::placeholder_t<4> _4; inline constexpr aux::placeholder_t<5> _5; inline constexpr aux::placeholder_t<6> _6; inline constexpr aux::placeholder_t<7> _7; inline constexpr aux::placeholder_t<8> _8; } /** * 20.9.11, member function adaptors: */ namespace aux { template class mem_fn_t { // TODO: conditional typedefs public: mem_fn_t(F f) : func_{f} { /* DUMMY BODY */ } template decltype(auto) operator()(Args&&... args) { return invoke(func_, forward(args)...); } private: F func_; }; } template aux::mem_fn_t mem_fn(R T::* f) { return aux::mem_fn_t{f}; } /** * 20.9.13, hash function primary template: */ namespace aux { template union converter { T value; uint64_t converted; }; template T hash_(uint64_t x) noexcept { /** * Note: std::hash is used for indexing in * unordered containers, not for cryptography. * Because of this, we decided to simply convert * the value to uin64_t, which will help us * with testing (since in order to create * a collision in a multiset or multimap * we simply need 2 values that congruent * by the size of the table. */ return static_cast(x); } template size_t hash(T x) noexcept { static_assert(is_arithmetic_v || is_pointer_v, "invalid type passed to aux::hash"); converter conv; conv.value = x; return hash_(conv.converted); } } template struct hash { /* DUMMY BODY */ }; template<> struct hash { size_t operator()(bool x) const noexcept { return aux::hash(x); } using argument_type = bool; using result_type = size_t; }; template<> struct hash { size_t operator()(char x) const noexcept { return aux::hash(x); } using argument_type = char; using result_type = size_t; }; template<> struct hash { size_t operator()(signed char x) const noexcept { return aux::hash(x); } using argument_type = signed char; using result_type = size_t; }; template<> struct hash { size_t operator()(unsigned char x) const noexcept { return aux::hash(x); } using argument_type = unsigned char; using result_type = size_t; }; template<> struct hash { size_t operator()(char16_t x) const noexcept { return aux::hash(x); } using argument_type = char16_t; using result_type = size_t; }; template<> struct hash { size_t operator()(char32_t x) const noexcept { return aux::hash(x); } using argument_type = char32_t; using result_type = size_t; }; template<> struct hash { size_t operator()(wchar_t x) const noexcept { return aux::hash(x); } using argument_type = wchar_t; using result_type = size_t; }; template<> struct hash { size_t operator()(short x) const noexcept { return aux::hash(x); } using argument_type = short; using result_type = size_t; }; template<> struct hash { size_t operator()(unsigned short x) const noexcept { return aux::hash(x); } using argument_type = unsigned short; using result_type = size_t; }; template<> struct hash { size_t operator()(int x) const noexcept { return aux::hash(x); } using argument_type = int; using result_type = size_t; }; template<> struct hash { size_t operator()(unsigned int x) const noexcept { return aux::hash(x); } using argument_type = unsigned int; using result_type = size_t; }; template<> struct hash { size_t operator()(long x) const noexcept { return aux::hash(x); } using argument_type = long; using result_type = size_t; }; template<> struct hash { size_t operator()(long long x) const noexcept { return aux::hash(x); } using argument_type = long long; using result_type = size_t; }; template<> struct hash { size_t operator()(unsigned long x) const noexcept { return aux::hash(x); } using argument_type = unsigned long; using result_type = size_t; }; template<> struct hash { size_t operator()(unsigned long long x) const noexcept { return aux::hash(x); } using argument_type = unsigned long long; using result_type = size_t; }; template<> struct hash { size_t operator()(float x) const noexcept { return aux::hash(x); } using argument_type = float; using result_type = size_t; }; template<> struct hash { size_t operator()(double x) const noexcept { return aux::hash(x); } using argument_type = double; using result_type = size_t; }; template<> struct hash { size_t operator()(long double x) const noexcept { return aux::hash(x); } using argument_type = long double; using result_type = size_t; }; template struct hash { size_t operator()(T* x) const noexcept { return aux::hash(x); } using argument_type = T*; using result_type = size_t; }; } #endif