Path: blob/master/dep/rapidyaml/include/c4/memory_util.hpp
4261 views
#ifndef _C4_MEMORY_UTIL_HPP_1#define _C4_MEMORY_UTIL_HPP_23#include "c4/config.hpp"4#include "c4/error.hpp"5#include "c4/compiler.hpp"6#include "c4/cpu.hpp"7#ifdef C4_MSVC8#include <intrin.h>9#endif10#include <string.h>1112#if (defined(__GNUC__) && __GNUC__ >= 10) || defined(__has_builtin)13#define _C4_USE_LSB_INTRINSIC(which) __has_builtin(which)14#define _C4_USE_MSB_INTRINSIC(which) __has_builtin(which)15#elif defined(C4_MSVC)16#define _C4_USE_LSB_INTRINSIC(which) true17#define _C4_USE_MSB_INTRINSIC(which) true18#else19// let's try our luck20#define _C4_USE_LSB_INTRINSIC(which) true21#define _C4_USE_MSB_INTRINSIC(which) true22#endif232425/** @file memory_util.hpp Some memory utilities. */2627namespace c4 {2829C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")3031/** set the given memory to zero */32C4_ALWAYS_INLINE void mem_zero(void* mem, size_t num_bytes)33{34memset(mem, 0, num_bytes);35}36/** set the given memory to zero */37template<class T>38C4_ALWAYS_INLINE void mem_zero(T* mem, size_t num_elms)39{40memset(mem, 0, sizeof(T) * num_elms);41}42/** set the given memory to zero */43template<class T>44C4_ALWAYS_INLINE void mem_zero(T* mem)45{46memset(mem, 0, sizeof(T));47}4849C4_ALWAYS_INLINE C4_CONST bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb)50{51// thanks @timwynants52return (((const char*)b + szb) > a && b < ((const char*)a+sza));53}5455void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times);565758//-----------------------------------------------------------------------------59//-----------------------------------------------------------------------------60//-----------------------------------------------------------------------------6162template<class T>63C4_ALWAYS_INLINE C4_CONST bool is_aligned(T *ptr, uintptr_t alignment=alignof(T))64{65return (uintptr_t(ptr) & (alignment - uintptr_t(1))) == uintptr_t(0);66}676869//-----------------------------------------------------------------------------70//-----------------------------------------------------------------------------71//-----------------------------------------------------------------------------72// least significant bit7374/** @name msb Compute the least significant bit75* @note the input value must be nonzero76* @note the input type must be unsigned77*/78/** @{ */7980// https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear81#define _c4_lsb_fallback \82unsigned c = 0; \83v = (v ^ (v - 1)) >> 1; /* Set v's trailing 0s to 1s and zero rest */ \84for(; v; ++c) \85v >>= 1; \86return (unsigned) c8788// u889template<class I>90C4_CONSTEXPR1491auto lsb(I v) noexcept92-> typename std::enable_if<sizeof(I) == 1u, unsigned>::type93{94C4_STATIC_ASSERT(std::is_unsigned<I>::value);95C4_ASSERT(v != 0);96#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)97// upcast to use the intrinsic, it's cheaper.98#ifdef C4_MSVC99#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)100unsigned long bit;101_BitScanForward(&bit, (unsigned long)v);102return bit;103#else104_c4_lsb_fallback;105#endif106#else107return (unsigned)__builtin_ctz((unsigned)v);108#endif109#else110_c4_lsb_fallback;111#endif112}113114// u16115template<class I>116C4_CONSTEXPR14117auto lsb(I v) noexcept118-> typename std::enable_if<sizeof(I) == 2u, unsigned>::type119{120C4_STATIC_ASSERT(std::is_unsigned<I>::value);121C4_ASSERT(v != 0);122#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)123// upcast to use the intrinsic, it's cheaper.124// Then remember that the upcast makes it to 31bits125#ifdef C4_MSVC126#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)127unsigned long bit;128_BitScanForward(&bit, (unsigned long)v);129return bit;130#else131_c4_lsb_fallback;132#endif133#else134return (unsigned)__builtin_ctz((unsigned)v);135#endif136#else137_c4_lsb_fallback;138#endif139}140141// u32142template<class I>143C4_CONSTEXPR14144auto lsb(I v) noexcept145-> typename std::enable_if<sizeof(I) == 4u, unsigned>::type146{147C4_STATIC_ASSERT(std::is_unsigned<I>::value);148C4_ASSERT(v != 0);149#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)150#ifdef C4_MSVC151#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)152unsigned long bit;153_BitScanForward(&bit, v);154return bit;155#else156_c4_lsb_fallback;157#endif158#else159return (unsigned)__builtin_ctz((unsigned)v);160#endif161#else162_c4_lsb_fallback;163#endif164}165166// u64 in 64bits167template<class I>168C4_CONSTEXPR14169auto lsb(I v) noexcept170-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type171{172C4_STATIC_ASSERT(std::is_unsigned<I>::value);173C4_ASSERT(v != 0);174#if _C4_USE_LSB_INTRINSIC(__builtin_ctzl)175#if defined(C4_MSVC)176#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)177unsigned long bit;178_BitScanForward64(&bit, v);179return bit;180#else181_c4_lsb_fallback;182#endif183#else184return (unsigned)__builtin_ctzl((unsigned long)v);185#endif186#else187_c4_lsb_fallback;188#endif189}190191// u64 in 32bits192template<class I>193C4_CONSTEXPR14194auto lsb(I v) noexcept195-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type196{197C4_STATIC_ASSERT(std::is_unsigned<I>::value);198C4_ASSERT(v != 0);199#if _C4_USE_LSB_INTRINSIC(__builtin_ctzll)200#if defined(C4_MSVC)201#if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)202unsigned long bit;203_BitScanForward64(&bit, v);204return bit;205#else206_c4_lsb_fallback;207#endif208#else209return (unsigned)__builtin_ctzll((unsigned long long)v);210#endif211#else212_c4_lsb_fallback;213#endif214}215216#undef _c4_lsb_fallback217218/** @} */219220221namespace detail {222template<class I, I val, unsigned num_bits, bool finished> struct _lsb11;223template<class I, I val, unsigned num_bits>224struct _lsb11<I, val, num_bits, false>225{226enum : unsigned { num = _lsb11<I, (val>>1), num_bits+I(1), (((val>>1)&I(1))!=I(0))>::num };227};228template<class I, I val, unsigned num_bits>229struct _lsb11<I, val, num_bits, true>230{231enum : unsigned { num = num_bits };232};233} // namespace detail234235236/** TMP version of lsb(); this needs to be implemented with template237* meta-programming because C++11 cannot use a constexpr function with238* local variables239* @see lsb */240template<class I, I number>241struct lsb11242{243static_assert(number != 0, "lsb: number must be nonzero");244enum : unsigned { value = detail::_lsb11<I, number, 0, ((number&I(1))!=I(0))>::num};245};246247248//-----------------------------------------------------------------------------249//-----------------------------------------------------------------------------250//-----------------------------------------------------------------------------251// most significant bit252253254/** @name msb Compute the most significant bit255* @note the input value must be nonzero256* @note the input type must be unsigned257*/258/** @{ */259260261#define _c4_msb8_fallback \262unsigned n = 0; \263if(v & I(0xf0)) v >>= 4, n |= I(4); \264if(v & I(0x0c)) v >>= 2, n |= I(2); \265if(v & I(0x02)) v >>= 1, n |= I(1); \266return n267268#define _c4_msb16_fallback \269unsigned n = 0; \270if(v & I(0xff00)) v >>= 8, n |= I(8); \271if(v & I(0x00f0)) v >>= 4, n |= I(4); \272if(v & I(0x000c)) v >>= 2, n |= I(2); \273if(v & I(0x0002)) v >>= 1, n |= I(1); \274return n275276#define _c4_msb32_fallback \277unsigned n = 0; \278if(v & I(0xffff0000)) v >>= 16, n |= 16; \279if(v & I(0x0000ff00)) v >>= 8, n |= 8; \280if(v & I(0x000000f0)) v >>= 4, n |= 4; \281if(v & I(0x0000000c)) v >>= 2, n |= 2; \282if(v & I(0x00000002)) v >>= 1, n |= 1; \283return n284285#define _c4_msb64_fallback \286unsigned n = 0; \287if(v & I(0xffffffff00000000)) v >>= 32, n |= I(32); \288if(v & I(0x00000000ffff0000)) v >>= 16, n |= I(16); \289if(v & I(0x000000000000ff00)) v >>= 8, n |= I(8); \290if(v & I(0x00000000000000f0)) v >>= 4, n |= I(4); \291if(v & I(0x000000000000000c)) v >>= 2, n |= I(2); \292if(v & I(0x0000000000000002)) v >>= 1, n |= I(1); \293return n294295296// u8297template<class I>298C4_CONSTEXPR14299auto msb(I v) noexcept300-> typename std::enable_if<sizeof(I) == 1u, unsigned>::type301{302C4_STATIC_ASSERT(std::is_unsigned<I>::value);303C4_ASSERT(v != 0);304#if _C4_USE_MSB_INTRINSIC(__builtin_clz)305// upcast to use the intrinsic, it's cheaper.306// Then remember that the upcast makes it to 31bits307#ifdef C4_MSVC308#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)309unsigned long bit;310_BitScanReverse(&bit, (unsigned long)v);311return bit;312#else313_c4_msb8_fallback;314#endif315#else316return 31u - (unsigned)__builtin_clz((unsigned)v);317#endif318#else319_c4_msb8_fallback;320#endif321}322323// u16324template<class I>325C4_CONSTEXPR14326auto msb(I v) noexcept327-> typename std::enable_if<sizeof(I) == 2u, unsigned>::type328{329C4_STATIC_ASSERT(std::is_unsigned<I>::value);330C4_ASSERT(v != 0);331#if _C4_USE_MSB_INTRINSIC(__builtin_clz)332// upcast to use the intrinsic, it's cheaper.333// Then remember that the upcast makes it to 31bits334#ifdef C4_MSVC335#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)336unsigned long bit;337_BitScanReverse(&bit, (unsigned long)v);338return bit;339#else340_c4_msb16_fallback;341#endif342#else343return 31u - (unsigned)__builtin_clz((unsigned)v);344#endif345#else346_c4_msb16_fallback;347#endif348}349350// u32351template<class I>352C4_CONSTEXPR14353auto msb(I v) noexcept354-> typename std::enable_if<sizeof(I) == 4u, unsigned>::type355{356C4_STATIC_ASSERT(std::is_unsigned<I>::value);357C4_ASSERT(v != 0);358#if _C4_USE_MSB_INTRINSIC(__builtin_clz)359#ifdef C4_MSVC360#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)361unsigned long bit;362_BitScanReverse(&bit, v);363return bit;364#else365_c4_msb32_fallback;366#endif367#else368return 31u - (unsigned)__builtin_clz((unsigned)v);369#endif370#else371_c4_msb32_fallback;372#endif373}374375// u64 in 64bits376template<class I>377C4_CONSTEXPR14378auto msb(I v) noexcept379-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type380{381C4_STATIC_ASSERT(std::is_unsigned<I>::value);382C4_ASSERT(v != 0);383#if _C4_USE_MSB_INTRINSIC(__builtin_clzl)384#ifdef C4_MSVC385#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)386unsigned long bit;387_BitScanReverse64(&bit, v);388return bit;389#else390_c4_msb64_fallback;391#endif392#else393return 63u - (unsigned)__builtin_clzl((unsigned long)v);394#endif395#else396_c4_msb64_fallback;397#endif398}399400// u64 in 32bits401template<class I>402C4_CONSTEXPR14403auto msb(I v) noexcept404-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type405{406C4_STATIC_ASSERT(std::is_unsigned<I>::value);407C4_ASSERT(v != 0);408#if _C4_USE_MSB_INTRINSIC(__builtin_clzll)409#ifdef C4_MSVC410#if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)411unsigned long bit;412_BitScanReverse64(&bit, v);413return bit;414#else415_c4_msb64_fallback;416#endif417#else418return 63u - (unsigned)__builtin_clzll((unsigned long long)v);419#endif420#else421_c4_msb64_fallback;422#endif423}424425#undef _c4_msb8_fallback426#undef _c4_msb16_fallback427#undef _c4_msb32_fallback428#undef _c4_msb64_fallback429430/** @} */431432433namespace detail {434template<class I, I val, I num_bits, bool finished> struct _msb11;435template<class I, I val, I num_bits>436struct _msb11< I, val, num_bits, false>437{438enum : unsigned { num = _msb11<I, (val>>1), num_bits+I(1), ((val>>1)==I(0))>::num };439};440template<class I, I val, I num_bits>441struct _msb11<I, val, num_bits, true>442{443static_assert(val == 0, "bad implementation");444enum : unsigned { num = (unsigned)(num_bits-1) };445};446} // namespace detail447448449/** TMP version of msb(); this needs to be implemented with template450* meta-programming because C++11 cannot use a constexpr function with451* local variables452* @see msb */453template<class I, I number>454struct msb11455{456enum : unsigned { value = detail::_msb11<I, number, 0, (number==I(0))>::num };457};458459460461#undef _C4_USE_LSB_INTRINSIC462#undef _C4_USE_MSB_INTRINSIC463464//-----------------------------------------------------------------------------465//-----------------------------------------------------------------------------466//-----------------------------------------------------------------------------467468// there is an implicit conversion below; it happens when E or B are469// narrower than int, and thus any operation will upcast the result to470// int, and then downcast to assign471C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wconversion")472473/** integer power; this function is constexpr-14 because of the local474* variables */475template<class B, class E>476C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type477{478C4_STATIC_ASSERT(std::is_integral<E>::value);479B r = B(1);480if(exponent >= 0)481{482for(E e = 0; e < exponent; ++e)483r *= base;484}485else486{487exponent *= E(-1);488for(E e = 0; e < exponent; ++e)489r /= base;490}491return r;492}493494/** integer power; this function is constexpr-14 because of the local495* variables */496template<class B, B base, class E>497C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type498{499C4_STATIC_ASSERT(std::is_integral<E>::value);500B r = B(1);501if(exponent >= 0)502{503for(E e = 0; e < exponent; ++e)504r *= base;505}506else507{508exponent *= E(-1);509for(E e = 0; e < exponent; ++e)510r /= base;511}512return r;513}514515/** integer power; this function is constexpr-14 because of the local516* variables */517template<class B, class Base, Base base, class E>518C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type519{520C4_STATIC_ASSERT(std::is_integral<E>::value);521B r = B(1);522B bbase = B(base);523if(exponent >= 0)524{525for(E e = 0; e < exponent; ++e)526r *= bbase;527}528else529{530exponent *= E(-1);531for(E e = 0; e < exponent; ++e)532r /= bbase;533}534return r;535}536537/** integer power; this function is constexpr-14 because of the local538* variables */539template<class B, class E>540C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type541{542C4_STATIC_ASSERT(std::is_integral<E>::value);543B r = B(1);544for(E e = 0; e < exponent; ++e)545r *= base;546return r;547}548549/** integer power; this function is constexpr-14 because of the local550* variables */551template<class B, B base, class E>552C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type553{554C4_STATIC_ASSERT(std::is_integral<E>::value);555B r = B(1);556for(E e = 0; e < exponent; ++e)557r *= base;558return r;559}560/** integer power; this function is constexpr-14 because of the local561* variables */562template<class B, class Base, Base base, class E>563C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type564{565C4_STATIC_ASSERT(std::is_integral<E>::value);566B r = B(1);567B bbase = B(base);568for(E e = 0; e < exponent; ++e)569r *= bbase;570return r;571}572573C4_SUPPRESS_WARNING_GCC_CLANG_POP574575576//-----------------------------------------------------------------------------577//-----------------------------------------------------------------------------578//-----------------------------------------------------------------------------579580/** return a mask with all bits set [first_bit,last_bit[; this function581* is constexpr-14 because of the local variables */582template<class I>583C4_CONSTEXPR14 I contiguous_mask(I first_bit, I last_bit)584{585I r = 0;586for(I i = first_bit; i < last_bit; ++i)587{588r |= (I(1) << i);589}590return r;591}592593594namespace detail {595596template<class I, I val, I first, I last, bool finished>597struct _ctgmsk11;598599template<class I, I val, I first, I last>600struct _ctgmsk11< I, val, first, last, true>601{602enum : I { value = _ctgmsk11<I, val|(I(1)<<first), first+I(1), last, (first+1!=last)>::value };603};604605template<class I, I val, I first, I last>606struct _ctgmsk11< I, val, first, last, false>607{608enum : I { value = val };609};610611} // namespace detail612613614/** TMP version of contiguous_mask(); this needs to be implemented with template615* meta-programming because C++11 cannot use a constexpr function with616* local variables617* @see contiguous_mask */618template<class I, I first_bit, I last_bit>619struct contiguous_mask11620{621enum : I { value = detail::_ctgmsk11<I, I(0), first_bit, last_bit, (first_bit!=last_bit)>::value };622};623624625//-----------------------------------------------------------------------------626//-----------------------------------------------------------------------------627//-----------------------------------------------------------------------------628/** use Empty Base Class Optimization to reduce the size of a pair of629* potentially empty types*/630631namespace detail {632typedef enum {633tpc_same,634tpc_same_empty,635tpc_both_empty,636tpc_first_empty,637tpc_second_empty,638tpc_general639} TightPairCase_e;640641template<class First, class Second>642constexpr TightPairCase_e tpc_which_case()643{644return std::is_same<First, Second>::value ?645std::is_empty<First>::value ?646tpc_same_empty647:648tpc_same649:650std::is_empty<First>::value && std::is_empty<Second>::value ?651tpc_both_empty652:653std::is_empty<First>::value ?654tpc_first_empty655:656std::is_empty<Second>::value ?657tpc_second_empty658:659tpc_general660;661}662663template<class First, class Second, TightPairCase_e Case>664struct tight_pair665{666private:667668First m_first;669Second m_second;670671public:672673using first_type = First;674using second_type = Second;675676tight_pair() : m_first(), m_second() {}677tight_pair(First const& f, Second const& s) : m_first(f), m_second(s) {}678679C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; }680C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; }681C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }682C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }683};684685template<class First, class Second>686struct tight_pair<First, Second, tpc_same_empty> : public First687{688static_assert(std::is_same<First, Second>::value, "bad implementation");689690using first_type = First;691using second_type = Second;692693tight_pair() : First() {}694tight_pair(First const& f, Second const& /*s*/) : First(f) {}695696C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }697C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }698C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return reinterpret_cast<Second &>(*this); }699C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return reinterpret_cast<Second const&>(*this); }700};701702template<class First, class Second>703struct tight_pair<First, Second, tpc_both_empty> : public First, public Second704{705using first_type = First;706using second_type = Second;707708tight_pair() : First(), Second() {}709tight_pair(First const& f, Second const& s) : First(f), Second(s) {}710711C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }712C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }713C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); }714C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); }715};716717template<class First, class Second>718struct tight_pair<First, Second, tpc_same> : public First719{720Second m_second;721722using first_type = First;723using second_type = Second;724725tight_pair() : First() {}726tight_pair(First const& f, Second const& s) : First(f), m_second(s) {}727728C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }729C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }730C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }731C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }732};733734template<class First, class Second>735struct tight_pair<First, Second, tpc_first_empty> : public First736{737Second m_second;738739using first_type = First;740using second_type = Second;741742tight_pair() : First(), m_second() {}743tight_pair(First const& f, Second const& s) : First(f), m_second(s) {}744745C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }746C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }747C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }748C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }749};750751template<class First, class Second>752struct tight_pair<First, Second, tpc_second_empty> : public Second753{754First m_first;755756using first_type = First;757using second_type = Second;758759tight_pair() : Second(), m_first() {}760tight_pair(First const& f, Second const& s) : Second(s), m_first(f) {}761762C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; }763C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; }764C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); }765C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); }766};767768} // namespace detail769770template<class First, class Second>771using tight_pair = detail::tight_pair<First, Second, detail::tpc_which_case<First,Second>()>;772773C4_SUPPRESS_WARNING_GCC_CLANG_POP774775} // namespace c4776777#endif /* _C4_MEMORY_UTIL_HPP_ */778779780