Implementing custom allocator I found out, that for classes which overloads void * operator new(std::size_t) and void operator delete(void *) allocator std::allocator not uses overloaded versions of mentioned operators, but it uses global defined versions of them instead. Documentation of std::allocator said:
Allocates
n * sizeof(T)bytes of uninitialized storage by calling::operator new(std::size_t), but it is unspecified when and how this function is called.
I think it is possible to make an overload using SFINAE by std::is_class< value_type >{} || std::is_union< value_type >{} or something similar, to dispatch on whether std::allocator_traits< A >::value_type can (ever) provide overloaded versions of the operators and eventually using proper version of them. But it is not enough to use the predicate in such a form.
#include <type_traits>
#include <memory>
#include <new>
#include <vector>
#include <iostream>
#include <cstdlib>
#include <cstddef>
#define PP { std::cout << __PRETTY_FUNCTION__ << std::endl; }
#define ENABLE std::enable_if_t< (std::is_class< type >{} || std::is_union< type >{}) > * = nullptr
#define DISABLE std::enable_if_t< !(std::is_class< type >{} || std::is_union< type >{}) > * = nullptr
template< typename type >
void *
operator new(std::size_t n, ENABLE) noexcept(false)
{
return type::operator new(n);
}
template< typename type >
void *
operator new(std::size_t n, DISABLE) noexcept(false)
{
PP;
return ::operator new(n);
}
template< typename type >
void
operator delete(void * p, ENABLE) noexcept
{
return type::operator delete(p);
}
template< typename type >
void
operator delete(void * p, DISABLE) noexcept
{
PP;
return ::operator delete(p);
}
template< typename type >
struct allocator
{
using value_type = type;
value_type *
allocate(std::size_t n) noexcept(false)
{
return static_cast< value_type * >(::operator new< value_type >(n));
}
void
deallocate(value_type * p, std::size_t /*n*/) noexcept
{
return ::operator delete< value_type >(p);
}
template< typename rhs >
constexpr
bool
operator == (allocator< rhs > const & /*_rhs*/) noexcept
{
return true;
}
template< typename rhs >
constexpr
bool
operator != (allocator< rhs > const & _rhs) noexcept
{
return !operator == (_rhs);
}
};
struct A
{
void *
operator new(std::size_t n) noexcept(false)
{
PP;
return ::operator new(n);
}
void
operator delete(void * p) noexcept
{
PP;
return ::operator delete(p);
}
};
struct B {};
int
main()
{
{
std::vector< A, allocator< A > > a;
a.resize(1);
}
std::cout << std::endl;
{
std::vector< int, allocator< int > > i;
i.resize(1);
}
std::cout << std::endl;
{
//std::vector< B, allocator< B > > b;
//b.resize(1);
}
return EXIT_SUCCESS;
}
The output for A type and int type is:
static void *A::operator new(std::size_t)
static void A::operator delete(void *)
void *operator new(std::size_t, std::enable_if_t<!(std::is_class<type>({}) || std::is_union<type>({}))> *) [type = int]
void operator delete(void *, std::enable_if_t<!(std::is_class<type>({}) || std::is_union<type>({}))> *) [type = int]
I.e. dispatching is right, but for B class an error occured:
main.cpp:34:22: error: no member named 'operator delete' in 'B'
return type::operator delete(p);
~~~~~~^
main.cpp:60:22: note: in instantiation of function template specialization 'operator delete<B>' requested here
return ::operator delete< value_type >(p);
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/bits/alloc_traits.h:386:13: note: in instantiation of member function 'allocator<B>::deallocate' requested here
{ __a.deallocate(__p, __n); }
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/bits/stl_vector.h:178:9: note: in instantiation of member function 'std::allocator_traits<allocator<B> >::deallocate' requested here
_Tr::deallocate(_M_impl, __p, __n);
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/bits/stl_vector.h:160:9: note: in instantiation of member function 'std::_Vector_base<B, allocator<B> >::_M_deallocate' requested here
{ _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/bits/stl_vector.h:253:7: note: in instantiation of member function 'std::_Vector_base<B, allocator<B> >::~_Vector_base' requested here
vector()
^
main.cpp:116:46: note: in instantiation of member function 'std::vector<B, allocator<B> >::vector' requested here
std::vector< B, allocator< B > > b;
^
main.cpp:19:22: error: no member named 'operator new' in 'B'
return type::operator new(n);
~~~~~~^
main.cpp:54:50: note: in instantiation of function template specialization 'operator new<B>' requested here
return static_cast< value_type * >(::operator new< value_type >(n));
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/bits/alloc_traits.h:360:20: note: in instantiation of member function 'allocator<B>::allocate' requested here
{ return __a.allocate(__n); }
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/bits/stl_vector.h:170:25: note: in instantiation of member function 'std::allocator_traits<allocator<B> >::allocate' requested here
return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/bits/vector.tcc:557:34: note: in instantiation of member function 'std::_Vector_base<B, allocator<B> >::_M_allocate' requested here
pointer __new_start(this->_M_allocate(__len));
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/bits/stl_vector.h:676:4: note: in instantiation of member function 'std::vector<B, allocator<B> >::_M_default_append' requested here
_M_default_append(__new_size - size());
^
main.cpp:117:15: note: in instantiation of member function 'std::vector<B, allocator<B> >::resize' requested here
b.resize(1);
^
2 errors generated.
How to detect the presence of overloaded versions of class-scope operator new and operator delete?
Aucun commentaire:
Enregistrer un commentaire