With containers containing heterogeneous types, e.g. struct S1
below, I'm propagating the allocator to the member elements via rebinding the allocator from the upstream obtained as part of the constructor [same would apply to extended copy and move constructors that take an allocator as an argument]. The question is if this is the idiomatic way to propagate the allocator or if there are other ways than through explicit invocation of allocator_traits?
#include <iostream>
#include <cassert>
#include <vector>
#include <scoped_allocator>
// Move allocator and container aliases into namepsace
namespace custom
{
template <typename T>
struct MyAlloc
{
using value_type = T;
MyAlloc(const std::string &scope) noexcept : _scope(scope) {}
// Rebinding allocatos to different type
template <class U>
MyAlloc(const MyAlloc<U> & other) noexcept : _scope(other._scope) {}
// Allow for move operations to be noexcept
//using is_always_equal = std::true_type;
value_type* allocate(std::size_t n) noexcept
{
std::cout << "Allocating " << n << " objects within " << _scope << " from " << __PRETTY_FUNCTION__ << std::endl;
return static_cast<value_type*>(::operator new (n*sizeof(value_type)));
}
void deallocate(value_type* p, std::size_t n) noexcept
{
std::cout << "Deallocating " << n << " objects within " << _scope << " from " << __PRETTY_FUNCTION__ << std::endl;
::operator delete(p);
}
std::string _scope;
};
// Allocators compare equal to enable one allocator to de-allocate memory
// from another
template <typename T>
bool operator==(const MyAlloc<T> &x1, const MyAlloc<T> &x2) noexcept
{
return true;
}
template <typename T>
bool operator!=(const MyAlloc<T> &x1, const MyAlloc<T> &x2) noexcept
{
return !(x1 == x2);
}
template <typename T>
using allocator = std::scoped_allocator_adaptor<MyAlloc<T>>;
template <typename T> // adaptor to propagate
using vector = std::vector<T, allocator<T>>;
template <typename T>
using bstr = std::basic_string<T, std::char_traits<T>, allocator<T>>;
using string = bstr<char>;
}
struct S1
{
using allocator_type = custom::allocator<std::byte>;
S1(const allocator_type &alloc) : str("This is a very long string indeed..", std::allocator_traits<allocator_type>::rebind_alloc<char>(alloc)),
vec(std::allocator_traits<allocator_type>::rebind_alloc<float>(alloc))
{
vec.emplace_back();
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
S1(const S1 &other, const allocator_type &alloc) : str(other.str, std::allocator_traits<allocator_type>::rebind_alloc<char>(alloc)),
vec(std::allocator_traits<allocator_type>::rebind_alloc<float>(alloc))
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
custom::string str;
custom::vector<float> vec;
};
int main()
{
custom::allocator<std::byte> sc{"scope1"};
custom::vector<S1> cv(sc);
cv.emplace_back();
}
Aucun commentaire:
Enregistrer un commentaire