Reputation: 21418
The code below defines a custom allocator that allocates 32-byte aligned memory.
I compiled the code with Visual Studio 2010. The code compiles fine in release mode. In debug mode I get a compilation error, see below, that I do not understand. What is wrong with the code?
#include <memory>
#include <vector>
inline void* aligned_malloc(size_t n)
{
if(n == 0) return nullptr;
char* p = (char*)malloc(n + 32);
if(p == nullptr) return nullptr;
size_t offset = 32 - (size_t)p % 32;
p += offset;
*(p - 1) = (char)offset;
return p;
}
inline void aligned_free(void* p)
{
if(p == nullptr) return;
char offset = *((char*)p - 1);
free((char*)p - offset);
}
template<class T>
class AlignedAllocator {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
T* address(T& x) const { return &x; }
const T* address(const T& x) const { return &x; }
size_t max_size() const { return (size_t)(-1) / sizeof(T); }
T* allocate(size_t n, const T* = nullptr)
{
T* p = (T*)aligned_malloc(n * sizeof(T));
if(!p) throw std::bad_alloc();
return p;
}
void deallocate(pointer p, size_type) { aligned_free(p); }
void construct(T* p, const T& x) { new(p) T(x); }
void destroy(T* p) { p->~T(); }
template<class U> struct rebind { typedef AlignedAllocator<U> other; };
};
std::vector<int, AlignedAllocator<int>> v;
Compilation error in debug mode:
c:\program files (x86)\microsoft visual studio 10.0\vc\include\vector(441): error C2440: 'initializing' : cannot convert from 'AlignedAllocator<T>' to 'AlignedAllocator<T>'
with
[
T=int
]
and
[
T=std::_Container_proxy
]
No constructor could take the source type, or constructor overload resolution was ambiguous
c:\program files (x86)\microsoft visual studio 10.0\vc\include\vector(437) : while compiling class template member function 'std::_Vector_val<_Ty,_Alloc>::_Vector_val(_Alloc)'
with
[
_Ty=int,
_Alloc=AlignedAllocator<int>
]
c:\program files (x86)\microsoft visual studio 10.0\vc\include\vector(481) : see reference to class template instantiation 'std::_Vector_val<_Ty,_Alloc>' being compiled
with
[
_Ty=int,
_Alloc=AlignedAllocator<int>
]
c:\users\rade\desktop 5\higg boson repository\tmp\tmp.cpp(52) : see reference to class template instantiation 'std::vector<_Ty,_Ax>' being compiled
with
[
_Ty=int,
_Ax=AlignedAllocator<int>
]
Upvotes: 1
Views: 451
Reputation: 21418
Here is a working version of the code. It combines code from my question, from the answer by T.C. and some additional fixes. I'm posting it here, in case someone might find it useful.
The code has been tested with MSVS 2008 and MSVS 2010. It is written in C++98. Some changes might be needed to make it conform with C++11 allocator requirements.
#include <cstdlib>
#include <new>
inline void* alignedMalloc(size_t n)
{
char* p = (char*)std::malloc(n + 32);
if(!p) return 0;
size_t offset = 32 - (size_t)p % 32;
p += offset;
*(p - 1) = (char)offset;
return p;
}
inline void alignedFree(void* p)
{
if(!p) return;
char offset = *((char*)p - 1);
std::free((char*)p - offset);
}
template<class T>
class AlignedAllocator {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
AlignedAllocator() {}
template<class U> AlignedAllocator(const AlignedAllocator<U>&) {}
T* address(T& x) const { return &x; }
const T* address(const T& x) const { return &x; }
size_t max_size() const { return (size_t)(-1) / sizeof(T); }
T* allocate(size_t n, const T* = 0)
{
T* p = (T*)alignedMalloc(n * sizeof(T));
if(!p) throw std::bad_alloc();
return p;
}
void deallocate(T* p, size_t) { alignedFree(p); }
void construct(T* p, const T& x) { new(p) T(x); }
void destroy(T* p) { (void)p; p->~T(); } // (void)p silences spurious MSVS 2010 compiler warning
template<class U> struct rebind { typedef AlignedAllocator<U> other; };
};
template <class T>
bool operator==(const AlignedAllocator<T>&, const AlignedAllocator<T>&) { return true; }
template <class T>
bool operator!=(const AlignedAllocator<T>&, const AlignedAllocator<T>&) { return false; }
Upvotes: 0
Reputation: 137325
The allocator requirements, among other things, require that given an allocator type X
, any type U
, and
using Y = typename X::template rebind<U>::other;
you are able to construct an Y
from an object of type X
. (The short of it as applied to your code is that you must be able to construct an AlignedAllocator<Foo>
from an AlignedAllocator<Bar>
for arbitrary types Foo
and Bar
.) Your allocator doesn't satisfy that requirement. In debug mode the MSVC standard library rebinds your allocator to maintain some additional internal data for debugging purposes, while in release mode it doesn't do this, which is why you only see the compiler complain in debug mode.
The fix is straightforward - just add the necessary constructors:
AlignedAllocator() { }
template<class U> AlignedAllocator(const AlignedAllocator<U> &) { }
Upvotes: 3