Topic: User-defined allocators without default constructor
Author: non-existent@iobox.com ("Sergey P. Derevyago")
Date: Sat, 18 Feb 2006 04:17:45 GMT Raw View
1. The essence of the problem.
------------------------------
User-defined allocators without default constructor are not explicitly
supported by the standard but they can be supported just like std::vector
supports elements without default constructor.
As a result, there exist implementations that work well with such allocators
and implementations that don't.
2. The cause of the problem.
----------------------------
1) The standard doesn't explicitly state this intent but it should. In
particular, 20.1.5p5 explicitly state the intent w.r.t. the allocator
instances that compare non-equal. So it can similarly state the intent w.r.t.
the user-defined allocators without default constructor.
2) Some container operations are obviously underspecified. In particular,
21.3.7.1p2 tells:
template<class charT, class traits, class Allocator>
basic_string<charT,traits,Allocator> operator+(
const charT* lhs,
const basic_string<charT,traits,Allocator>& rhs
);
Returns: basic_string<charT,traits,Allocator>(lhs) + rhs.
That leads to the basic_string<charT,traits,Allocator>(lhs, Allocator()) call.
Obviously, the right requirement is:
Returns: basic_string<charT,traits,Allocator>(lhs, rhs.get_allocator()) + rhs.
It seems like a lot of DRs can be submitted on this "Absent call to
get_allocator()" topic.
3. Proposed actions.
--------------------
1) Explicitly state the intent to allow for user-defined allocators without
default constructor in 20.1.5 Allocator requirements.
2) Correct all the places, where a correct allocator object is available
through the get_allocator() call but default Allocator() gets passed instead.
4. Code sample.
---------------
Let's suppose that the following memory pool is available:
class mem_pool {
// ...
void* allocate(size_t size);
void deallocate(void* ptr, size_t size);
};
So the following allocator can be implemented via this pool:
class stl_allocator {
mem_pool& pool;
public:
explicit stl_allocator(mem_pool& mp) : pool(mp) {}
stl_allocator(const stl_allocator& sa) : pool(sa.pool) {}
template <class U>
stl_allocator(const stl_allocator<U>& sa) : pool(sa.get_pool()) {}
~stl_allocator() {}
pointer allocate(size_type n, std::allocator<void>::const_pointer = 0)
{
return (n!=0) ? static_cast<pointer>(pool.allocate(n*sizeof(T))) : 0;
}
void deallocate(pointer p, size_type n)
{
if (n!=0) pool.deallocate(p, n*sizeof(T));
}
// ...
};
Then the following code works well on some implementations and doesn't work on
another:
typedef basic_string<char, char_traits<char>, stl_allocator<char> >
tl_string;
mem_pool mp;
tl_string s1("abc", stl_allocator<int>(mp));
printf("(%s)\n", ("def"+s1).c_str());
In particular, on some implementations the code can't be compiled without
default stl_allocator() constructor.
The obvious way to solve the compile-time problems is to intentionally define
a NULL pointer dereferencing default constructor
stl_allocator() : pool(*static_cast<mem_pool*>(0)) {}
in a hope that it will not be called. The problem is that it really gets
called by operator+(const char*, const string&) under the current 21.3.7.1p2
wording.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Lance Diduck" <lancediduck@nyc.rr.com>
Date: Sun, 26 Feb 2006 19:46:08 CST Raw View
There are a number of discussions about "private heaps and the STL"
and indeed a recent proposal. I have found that the only way to keep
your sanity, and not have to rethink and reimplement the existing STL
implementations, is just not to allow for "default private heaps." and
let it break exactly as you describe. I have successfully gotten
applciations that use things as complicated as struct t1string
{t1_string a;t1_string b;}; t1_map<t1_string,t1string > to work using
this method.
Do a search for "N1850" on this newsgroup for more info.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Sergey P. Derevyago" <non-existent@iobox.com>
Date: Mon, 27 Feb 2006 19:55:46 CST Raw View
Lance Diduck wrote:
> Do a search for "N1850" on this newsgroup for more info.
>
Thank you, it helps a lot.
Now my vision is clear: there is no robust way to use user-defined allocators
without default constructor with current STL implementations. So I have to
implement my own versions of containers that do really use passed allocator
objects.
--
With all respect, Sergey. http://ders.angen.net/
mailto : ders at skeptik.net
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]