C++ custom STL container memory allocator (map, set, vector, string, unordered_map, unordered_set)

Notice:

When using the C++ 11/14 standard, you should comment the following code and restore the above commented code, that is, set the compiler option standard to: -std=cxx1z, -std=cxx2a.

 // C++17...cxx1z
        //template <class _Iter>
        //constexpr void* _Voidify_iter(_Iter _It) noexcept {
        // if constexpr (std::is_pointer<_Iter>::value) {
        // return const_cast<void*>(static_cast<const volatile void*>(_It));
        // }
        // else {
        // return const_cast<void*>(static_cast<const volatile void*>(std::addressof(*_It)));
        // }
        //}

        // C++11...cxx0x
        // C++14...cxx1y
        template <typename _Iter>
        typename std::enable_if<std::is_pointer<_Iter>::value, void*>::type
        constexpr _Voidify_iter(_Iter _It) noexcept {
            return const_cast<void*>(static_cast<const volatile void*>(_It));
        }

        template <typename _Iter>
        typename std::enable_if<!std::is_pointer<_Iter>::value, void*>::type
        constexpr _Voidify_iter(_Iter _It) noexcept {
            return const_cast<void*>(static_cast<const volatile void*>(std::addressof(*_It)));
        }

Of course you can choose to determine the compiler macro: __cplusplus

if (__cplusplus == 201703L)
    std::cout << "C + + 17\
";
else if (__cplusplus == 201402L)
    std::cout << "C + + 14\
";
else if (__cplusplus == 201103L)
    std::cout << "C + + 11\
";
else if (__cplusplus == 199711L)
    std::cout << "C + + 98\
";
else if (__cplusplus == 202002L)
    std::cout << "C + + 20\
";

else
    std::cout << "pre-standard C + + \
";

Header file definition:

 template 
    class allocator {
    public:
        static_assert(!std::is_const<_Ty>::value, "The C++ Standard forbids containers of const elements "
            "because allocator is ill-formed.");

        using _From_primary = allocator;

        using value_type = _Ty;

        using pointer = _Ty*;
        using const_pointer = const _Ty*;

        using reference = _Ty &;
        using const_reference = const _Ty &;

        using size_type = size_t;
        using difference_type = ptrdiff_t;

        using propagate_on_container_move_assignment = std::true_type;
        using is_always_equal = std::true_type;

        template 
        struct rebind {
            using other = allocator<_Other>;
        };

        _Ty* address(_Ty & amp; _Val) const noexcept {
            return std::addressof(_Val);
        }

        const _Ty* address(const _Ty & amp; _Val) const noexcept {
            return std::addressof(_Val);
        }

        constexpr allocator() noexcept {}
        constexpr allocator(const allocator & amp;) noexcept = default;

        template 
        constexpr allocator(const allocator<_Other> & amp;) noexcept {}

        ~allocator() = default;
        allocator & amp; operator=(const allocator & amp;) = default;

        void deallocate(_Ty* const _Ptr, const size_t _Count) {
            assert((_Ptr != nullptr || _Count == 0) & amp; & amp; "null pointer cannot point to a block of non-zero size");
            // no overflow check on the following multiply; we assume _Allocate did that check
            ppp::Mfree(_Ptr);
        }

        _Ty* allocate(const size_t _Count) {
            static_assert(sizeof(value_type) > 0, "value_type must be complete before calling allocate.");

            void* memory = (void*)ppp::Malloc(sizeof(_Ty) * _Count);
            return static_cast<_Ty*>(memory);
        }

        _Ty* allocate(const size_t _Count, const void*) {
            return allocate(_Count);
        }

        // C++17...cxx1z
        //template <class _Iter>
        //constexpr void* _Voidify_iter(_Iter _It) noexcept {
        // if constexpr (std::is_pointer<_Iter>::value) {
        // return const_cast<void*>(static_cast<const volatile void*>(_It));
        // }
        // else {
        // return const_cast<void*>(static_cast<const volatile void*>(std::addressof(*_It)));
        // }
        //}

        // C++11...cxx0x
        // C++14...cxx1y
        template <typename _Iter>
        typename std::enable_if<std::is_pointer<_Iter>::value, void*>::type
        constexpr _Voidify_iter(_Iter _It) noexcept {
            return const_cast<void*>(static_cast<const volatile void*>(_It));
        }

        template <typename _Iter>
        typename std::enable_if<!std::is_pointer<_Iter>::value, void*>::type
        constexpr _Voidify_iter(_Iter _It) noexcept {
            return const_cast<void*>(static_cast<const volatile void*>(std::addressof(*_It)));
        }

        template 
        void construct(_Objty* const _Ptr, _Types & amp; & amp;... _Args) {
            ::new (_Voidify_iter(_Ptr)) _Objty(std::forward<_Types>(_Args)...);
        }

        template 
        void destroy(_Uty* const _Ptr) {
            _Ptr->~_Uty();
        }

        size_t max_size() const noexcept {
            return static_cast(-1) / sizeof(_Ty);
        }

        // The number of user bytes a single byte of ASAN shadow memory can track.
        static constexpr size_t _Asan_granularity = 8;
        static constexpr size_t _Minimum_allocation_alignment = _Asan_granularity;
    };

    using string = std::basic_string, allocator>;

    template 
    using list = std::list>;

    template 
    using vector = std::vector>;

    template 
    using set = std::set, allocator>;

    template 
    using map = std::map, allocator>>;

    template 
    using unordered_set = std::unordered_set, std::equal_to, allocator>;

    template 
    using unordered_map = std::unordered_map, std::equal_to, allocator>>;