On 18 August 2011 09:27, Jeffrey Walton <noloader@gmail.com> wrote:
> On Thu, Aug 18, 2011 at 4:21 AM, Jonathan Wakely <jwakely.gcc@gmail.com>
wrote:
>> On 18 August 2011 08:55, Jeffrey Walton wrote:
>>> Hi Jonathan,
>>>
>>> Sorry to go offlist, but its off topic.
>>
>> This is a slightly trickier one. The problem is in an incomplete
>> C++0x implementation in libstdc++ 4.6, but there's a simple
>> workaround, full details to follow shortly ...
> OK. I found I could work around with the following. I wanted to make
> sure I was not screwing it up.
>
> Jeff
>
> // construction/destruction
> // inline void construct(pointer p, const T& t) { new(p) T(t); }
> // Added for Fedora 15/GCC 4.6. Was the previous construct non-compliant?
> inline void construct(pointer p, const T& t = T()) { new(p) T(t);
> inline void destroy(pointer p) { p->~T(); }
>
Your allocator is probably OK now you've made it CopyConstructible but there's
a problem when using custom allocators with the libstdc++ from GCC 4.6
I'm part way through writing a journal article for the ACCU explaining the
allocator changes in C++11, as I've not seen anyone try to document them, so
they must be inferred from the C++ standard. I'll let you know if/when I
finish writing that. Anyway ....
In C++03 the signature for std::allocator<T>::construct is:
void construct(pointer p, const T& t)
{
::new ((void*)p) T(t);
}
And custom allocators must have the same signature.
In C++11 std::allocator changes it to a variadic template (and note that the
type constructed is not necessarily the same as the allocator's value type,
it's deduced from the pointer type passed in as the first parameter):
template<typename T2, typename... Args>
void construct(T2* p, Args&&... a)
{
::new ((void*)p) T2(std::forward<Args>(a)...);
}
Custom allocators are not required to have the same signature, in fact in C++11
they are not required to provide a 'construct' member at all.
In order to retain backward compatibility with custom allocators using the old
signature, or with new C++11 allocators with no 'construct' there is a new
class template allocator_traits, which provides a fallback definition, which
should be used like so:
zallocator<T> z;
typedef allocator_traits<zallocator<T>> traits;
T* buf = traits::allocate(z, 3);
traits::construct(z, buf+0, t);
traits::construct(z, buf+1);
traits::construct(z, buf+2, 1, 2, 3);
The first construct call would use your zallocator's 'construct' member. The
second and third would use the default definition provided by allocator_traits,
because your zallocator doesn't have a suitable 'construct' member. This makes
everything work perfectly, whether using C++03-style allocators or C++11-style
allocators.
The problem is that the libstdc++ in GCC 4.6 doesn't have allocator_traits, so
the calls above would be done by calling your allocator's construct directly:
zallocator<T> z;
T* buf = z.allocate(3);
z.construct(buf+0, t);
z.construct(buf+1);
z.construct(buf+2, 1, 2, 3);
Obviously this fails if zallocator only provides the C++03 interface, which is
the error you're getting.
I've added allocator_traits so it will be in GCC 4.7, but until then the
workaround would be to add this to your allocator:
#if defined(__GXX_EXPERIMENTAL_CXX0X__) \
&& __GNUC__ == 4 && __GNUC_MINOR__ < 7
template<typename U, typename... Args>
void construct(U* p, Args&&... a)
{
::new ((void*)p) U(std::forward<Args>(a)...);
}
template<typename U>
void destroy(U* p)
{
delete p;
}
#endif
That should let your allocator work with any code (such as libsdc++ in gcc 4.6)
which incorrectly relies on the C++11 allocator interface instead of making use
of allocator_traits
Original issue reported on code.google.com by noloa...@gmail.com on 18 Aug 2011 at 1:38
Original issue reported on code.google.com by
noloa...@gmail.com
on 18 Aug 2011 at 1:38