I'm still learning allocators and I've struggled with the error that occurs, for example, on
std::vector::shrink_to_fit()
method call. Here is my allocator (I omitted some
(I believe) unimportant code for brevity):
#include <cinttypes>
#include <iostream>
#include <algorithm>
#include <vector>
#include <list>
#include <forward_list>
#include <array>
#include <map>
enum class MemoryBank : std::uint8_t
{
General,
Audio,
Video
};
template<std::size_t ChunkSize = 1024>
class ChunkMemoryManager
{
public:
ChunkMemoryManager() { std::cout << "ChunkMemoryManager()\n"; }
char* allocate(std::size_t bytes)
{
return nullptr;
/*skipped*/
}
void deallocate(char* address, size_t size)
{
/*skipped*/
}
private: // types
struct Chunk
{
Chunk() { freeSlots.emplace_front(0, ChunkSize); }
std::array<char, ChunkSize> memory;
std::forward_list<std::pair<std::size_t, std::size_t>> freeSlots;
};
private: // data
std::list<Chunk> chunks;
};
template<typename T, MemoryBank bank = MemoryBank::General, typename MemoryManager = ChunkMemoryManager<1024>>
class MemoryManagerAllocator
{
public:
using value_type = T;
using propagate_on_container_swap = std::true_type();
template<typename U>
struct rebind
{
using other = MemoryManagerAllocator<U, bank, MemoryManager>;
};
MemoryManagerAllocator() = default;
MemoryManagerAllocator(const MemoryManagerAllocator&) = default;
MemoryManagerAllocator& operator=(const MemoryManagerAllocator& rhs) = default;
template<class U, MemoryBank UMemoryBank, typename UMemoryManager>
constexpr MemoryManagerAllocator(const MemoryManagerAllocator<U, UMemoryBank, UMemoryManager>&) noexcept {};
[[nodiscard]] T* allocate(std::size_t n) { return reinterpret_cast<T*>(memoryManager.allocate(n * sizeof(T))); }
void deallocate(T* p, std::size_t n) noexcept
{
memoryManager.deallocate(reinterpret_cast<char*>(p), n * sizeof(T));
}
private:
inline static MemoryManager memoryManager;
};
template<class T, MemoryBank TMemoryBank, class TMemoryManager, class U, MemoryBank UMemoryBank, class UMemoryManager>
bool operator==(const MemoryManagerAllocator<T, TMemoryBank, TMemoryManager>&,
const MemoryManagerAllocator<U, UMemoryBank, UMemoryManager>&)
{
return true;
}
template<class T, MemoryBank TMemoryBank, class TMemoryManager, class U, MemoryBank UMemoryBank, class UMemoryManager>
bool operator!=(const MemoryManagerAllocator<T, TMemoryBank, TMemoryManager>&,
const MemoryManagerAllocator<U, UMemoryBank, UMemoryManager>&)
{
return false;
}
The driver code:
int main()
{
using MMAllocator = MemoryManagerAllocator<int>;
std::vector<int, MMAllocator> v;
v.shrink_to_fit();
}
gives the following error:
In file included from /usr/include/c++/13/cstdint:38,
from /usr/include/c++/13/cinttypes:38,
from /home/user/.vs/allocator/src/main.cpp:1:
/usr/include/c++/13/bits/stl_vector.h: In instantiation of ‘constexpr void std::vector<_Tp, _Alloc>::swap(std::vector<_Tp, _Alloc>&) [with _Tp = int; _Alloc = MemoryManagerAllocator<int>]’:
/usr/include/c++/13/bits/alloc_traits.h:908:28: required from ‘static constexpr bool std::__shrink_to_fit_aux<_Tp, true>::_S_do_it(_Tp&) [with _Tp = std::vector<int, MemoryManagerAllocator<int> >]’
/usr/include/c++/13/bits/vector.tcc:721:56: required from ‘constexpr bool std::vector<_Tp, _Alloc>::_M_shrink_to_fit() [with _Tp = int; _Alloc = MemoryManagerAllocator<int>]’
/usr/include/c++/13/bits/stl_vector.h:1069:9: required from ‘constexpr void std::vector<_Tp, _Alloc>::shrink_to_fit() [with _Tp = int; _Alloc = MemoryManagerAllocator<int>]’
/home/user/.vs/allocator/src/main.cpp:143:20: required from here
/usr/include/c++/13/bits/stl_vector.h:1589:9: error: ‘value’ is not a member of ‘std::allocator_traits<MemoryManagerAllocator<int> >::propagate_on_container_swap’ {aka ‘std::integral_constant<bool, true>()’}
1589 | __glibcxx_assert(_Alloc_traits::propagate_on_container_swap::value
| ^~~~~~~~~~~~~~~~
gmake[3]: *** [CMakeFiles/allocator.dir/build.make:76: CMakeFiles/allocator.dir/src/main.cpp.o] Error 1
If I understand it correctly, compiler can't swap my allocator, am I right? How to solve this error?
Update: I fixed the code so it will show the error I mentioned above. Also I've noticed that this code works in MSVC and doesn't work with GCC.
This line is wrong:
using propagate_on_container_swap = std::true_type();
You are defining your propagate_on_container_swap
as a function type, which takes no parameters and returns a std::true_type
.
However, std::allocator_traits
is expecting propagate_on_container_swap
to be defined as a class/struct type, which has a nested value
static member. std::true_type
and std::false_type
satisfy that requirement, so use this instead:
using propagate_on_container_swap = std::true_type;