'Getting weird compiler errors while compiling my own "allocator<>" with MSVC 2022 in Debug-mode

I need an std::allocator<> compliant allocator with page-aligned allocation and preferrably no memory-pooling for very large allocations. Another requirement is that the allocator should have a non-standard reset()-method to reset a row of pages that the pages become discardable by the kernel if you have a row of trivial data types.
I wrote such an allocator which I post here below completely since I think others could use the allocator also if they also have this certain allocation constraints.

#pragma once
#if defined(_WIN32)
    #include <Windows.h>
#elif defined(__unix__)
    #include <unistd.h>
    #include <sys/mman.h>
#else
    #error platform not supported
#endif
#include <cstddef>
#include <type_traits>
#include <memory>
#include <utility>
#include <atomic>
#include <cassert>
#include <new>
#if !defined(NDEBUG)
    #include <bit>
#endif
#include <utility>

template <typename T>
struct virtual_allocator
{
    using allocator_type = virtual_allocator;
    using value_type = T;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;
    using propagate_on_container_move_assignment = std::false_type;
    constexpr virtual_allocator() noexcept {}
    constexpr virtual_allocator( virtual_allocator const & ) noexcept {}
    static value_type *allocate( std::size_t n );
    static void deallocate( value_type *p, std::size_t n ) noexcept;
    static void reset( void *p, std::size_t n )
        requires (sizeof(T) == std::bit_floor( sizeof(T) )) && std::is_trivial_v<T>;
    static std::size_t get_page_size();
#if defined(__cpp_lib_allocate_at_least)
    static std::allocation_result<value_type *> allocate_at_least( std::size_t n );
#endif
private:
    using alloc_ret_t = std::pair<value_type *, std::size_t>;
    static alloc_ret_t allocateAtLeast( std::size_t n );
    static std::size_t getPageSize();
};

template <typename T>
typename virtual_allocator<T>::value_type *virtual_allocator<T>::allocate( std::size_t n )
{
    return allocateAtLeast( n ).first;
}

template <typename T>
void virtual_allocator<T>::deallocate( value_type *p, std::size_t n ) noexcept
{
    std::size_t pageSize = getPageSize();
    assert(n * pageSize / pageSize == n);
    n = n * pageSize + pageSize - 1 & -(ptrdiff_t)pageSize;
#if defined(_WIN32)
    bool succ = (bool)VirtualFree( p, 0, MEM_RELEASE );
#elif defined(__unix__)
    bool succ = !munmap( p, n );
#endif
    assert(succ);
}

#if defined(__cpp_lib_allocate_at_least)
template <typename T>
std::allocation_result<typename virtual_allocator<T>::value_type *> virtual_allocator<T>::allocate_at_least( std::size_t n )
{
    auto ret = allocateAtLeast( n );
    return std::allocation_result<value_type *>( ret.first, ret.second );
}
#endif

template <typename T>
typename virtual_allocator<T>::alloc_ret_t virtual_allocator<T>::allocateAtLeast( std::size_t n )
{
    using namespace std;
    if( n * sizeof(value_type) / sizeof(value_type) != n )
        throw bad_alloc();
    size_t pageSize = getPageSize();
    n = n * sizeof(value_type) + pageSize - 1 & -(ptrdiff_t)pageSize;
#if defined(_WIN32)
    value_type *p = (value_type *)VirtualAlloc( nullptr, n, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );
#elif defined(__unix__)
    value_type *p = (value_type *)mmap( nullptr, n, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0 );
#endif
    if( !p ) [[unlikely]]
        throw bad_alloc();
    return alloc_ret_t( p, n / sizeof(value_type) );
}

template <typename T>
void virtual_allocator<T>::reset( void *p, std::size_t n )
    requires (sizeof(T) == std::bit_floor( sizeof(T) )) && std::is_trivial_v<T>
{
    using namespace std;
    if( ((size_t)p | n) & getPageSize() - 1 || n * sizeof(value_type) / sizeof(value_type) != n ) [[unlikely]]
        throw bad_alloc();
#if defined(_WIN32)
    bool succ = (bool)VirtualAlloc( p, n, MEM_RESET, PAGE_READWRITE );
#elif defined(__unix__)
    bool succ = !madvise( p, n, MADV_DONTNEED );
#endif
    assert(succ);
}

template <typename T>
inline
std::size_t virtual_allocator<T>::get_page_size()
{
    return getPageSize();
}

template <typename T>
inline
std::size_t virtual_allocator<T>::getPageSize()
{
    using namespace std;
    static atomic<size_t> aPageSize( 0 );
    size_t pageSize = aPageSize.load( memory_order_relaxed );
    if( !pageSize ) [[unlikely]]
    {
#if defined(_WIN32)
        SYSTEM_INFO si;
        GetSystemInfo( &si );
        pageSize = si.dwPageSize;
#elif defined(__unix__)
        pageSize = sysconf( _SC_PAGESIZE );
#endif
        assert(pageSize && pageSize == bit_floor( pageSize ));
        aPageSize.store( pageSize, memory_order_relaxed );
    }
    return pageSize;
}

The testing code compiles fine with g++ 11.1.0 and clang++ 13 under Linux. Under Windows it compiles if you have a "Release" compile but not in "Debug" mode. I get some weird errors for which I don't have any clue what they're abut.

Severity    Code    Description Project File    Line    Suppression State
Error   C2440   'static_cast': cannot convert from 'virtual_allocator<array_t>' to 'virtual_allocator<_Newfirst>'   virtual_allocator   C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include\vector    595 
Error   C2530   '_Alproxy': references must be initialized  virtual_allocator   C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include\vector    595 
Error   C3536   '_Alproxy': cannot be used before it is initialized virtual_allocator   C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include\vector    596 
Error   C2672   '_Delete_plain_internal': no matching overloaded function found virtual_allocator   C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include\vector    596 
Error   C2893   Failed to specialize function template 'void std::_Delete_plain_internal(_Alloc &,_Alloc::value_type *const ) noexcept' virtual_allocator   C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include\vector    596 

Although I think the code would be helpful for other C++-developers I don't post this with any C++-tags. That's while if others search such an allcator they'll find it by googling anyway and my question is only related to Visual C++.



Solution 1:[1]

Yeah, Igor got it ! It was the missing templated constructor.

template<typename T2>
constexpr virtual_allocator( virtual_allocator<T2> const & ) noexcept {}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1