Topic: allocator as ctor argument versus part of the type


Author: llewelly.at@xmission.dot.com (llewelly)
Date: Mon, 26 Apr 2004 01:39:55 +0000 (UTC)
Raw View
apm35@student.open.ac.uk (apm) writes:

> Guys,
>
> Here are two ways to for my class Foo to have a
> vector of int using my custom allocator, apm_alloc:
>
> class Foo {
> std::vector<int, apm_alloc> myvec;
>     :
>     :
> };
>
> class Foo {
> std::vector<int> myvec;
>     :
>     :
> public:
> Foo(const apm_alloc& myalloc = apm_alloc())
>   : myvec(myalloc)
> {
>   // other ctor stuff
> }
>
> };
>
> My question is: what do people think of the second form?

AFAICT, the standard allows arbitrary instances of apm_alloc to be
    used for allocations and deallocations. The specific instance you
    construct the vector with could ignored, it could be used only
    for allocations, it could be use for some deallocations but not
    others, etc.

14th entry in table 32:

#    expression | return type | assertion/note pre/post-condition
#    ...        | ...         | ...
#    a1 == a2   | bool        | returns true iff storage allocated
#               |             | from each can be deallocated via the
#               |             | other .

20.1.5/5:

#    [...] the semantics of containers and algorithms when allocator
#    instances compare non-equal, are implementation-defined.

Scott's item 11 recomends allocators never be given per-object state.

However the implementation is allowed to provide more guarantees.

---
[ 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: AlbertoBarbati@libero.it (Alberto Barbati)
Date: Mon, 26 Apr 2004 18:30:38 +0000 (UTC)
Raw View
apm wrote:

> class Foo {
> std::vector<int> myvec;
>     :
>     :
> public:
> Foo(const apm_alloc& myalloc = apm_alloc())
>   : myvec(myalloc)
> {
>   // other ctor stuff
> }
>
> };

It shouldn't compile, unless apm_alloc is a typedef for
std::allocator<int> or derives from it. In the latter case you are
slicing the allocator object (which is being copied) and you are not
getting the behaviour you probably want because std::allocator<int>
member function are not virtual.

You should declare myvec as std::vector<int, apm_alloc>. IMHO, you
should also avoid deriving apm_alloc from std::allocator<whatever>.

Alberto Barbati

---
[ 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: apm35@student.open.ac.uk (apm)
Date: Tue, 27 Apr 2004 01:42:15 +0000 (UTC)
Raw View
AlbertoBarbati@libero.it (Alberto Barbati) wrote in message news:<h09jc.23097$Qc.929503@twister1.libero.it>...
> apm wrote:
> > Foo(const apm_alloc& myalloc = apm_alloc())
> >   : myvec(myalloc)
> > {
> >   // other ctor stuff
> > }

> It shouldn't compile, unless apm_alloc is a typedef for
> std::allocator<int> or derives from it. In the latter case you are
> slicing the allocator object (which is being copied) and you are not
> getting the behaviour you probably want because std::allocator<int>
> member function are not virtual.

Indeed. I have just worked this out after several days of
head-scratching. Thanks for the confirmation, this means AFAICS that
the ctor argument form is useless. So if I have a class that makes use
of STL variables that perform dynamic memory allocation then I have to
make my class a template class, templated on allocator. The allocator
itself will be templated on the type so I need a compiler that
supports template templates. Not sure if the commercial Unix compilers
I am using support that. Ho hum. Aren't custom allocators a can of
worms? Is the stds committee thinking about any changes in this area?

Regards,

Andrew M.

---
[ 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: Michael.Karcher@writeme.com (Michael Karcher)
Date: Tue, 27 Apr 2004 18:18:14 +0000 (UTC)
Raw View
apm <apm35@student.open.ac.uk> wrote:
> Indeed. I have just worked this out after several days of
> head-scratching. Thanks for the confirmation, this means AFAICS that
> the ctor argument form is useless.

If your apm_allocator is an allocator having non-default constructors (most
probably a state allocator), the ctor argument is needed to initialize the
allocator object. It is not used to set the allocator's type, but to set the
allocators data, so it is really useless for the std::allocator<> template
as far as the standard is concerned, as this class has just the default and
the copy constructor (still it could be stateful, for example holding back
deallocated objects for further allocates), but it is needed for
non-default-constructable allocators, like this one:

#include <limits>
#include <ostream>
#include <iostream>

template<typename T>
class debugging_allocator
{
 const char * myname;
public:
 typedef T* pointer;
 typedef const T* const_pointer;
 typedef T& reference;
 typedef T const& const_reference;
 typedef T value_type;
 typedef size_t size_type;
 typedef ptrdiff_t difference_type;
 template<typename U> struct rebind {
  typedef debugging_allocator<U> other;
 };
 pointer address(reference r) {return &r;}
 const_pointer address(const_reference r) {return &r;}
 pointer allocate(size_type n,const_pointer u = 0);
 void deallocate(pointer p, size_type n);
 size_type max_size()
  { return std::numeric_limits<size_type>::max() /
   sizeof(T); }
 debugging_allocator(const char * name = "Allocator") : myname(name) {};
 // copy ctr is OK
 // assignment is OK
 void construct(pointer p, const T& t)
  { new(static_cast<void*>(p)) T(t); }
 void destroy(pointer p) { p->~T(); }
};

template <typename T>
debugging_allocator<T>::pointer
 debugging_allocator<T>::allocate(size_type n, const_pointer)
{
 void * p = ::operator new(n * sizeof(T));
  // Do not use endl for efficiency
 std::clog << myname << " allocating: " << n << " -> " << p << '\n';
 return static_cast<pointer>(p);
}

template<typename T>
void debugging_allocator<T>::deallocate(pointer ptr, size_type n)
{
 void* p = static_cast<void*>(ptr);
 std::clog << myname << " deallocating: " << n << "  @ " << p << '\n';
 ::operator delete(p);
}

#include <vector>

int main(void)
{
 // *** HERE I'm using the allocator argument ***
 std::vector<int,debugging_allocator<int> >
  v1(debugging_allocator<int>("Alloc1"));
 // Here I pass a default size to see its influence on allocation
 std::vector<int,debugging_allocator<int> > v2(5);
 for(int i = 0;i < 100;i++)
 {
  v1.push_back(i);
  v2.push_back(i);
 }
}

> So if I have a class that makes use of STL variables that perform dynamic
> memory allocation then I have to make my class a template class, templated
> on allocator.
If you want to support different allocator types: yes. Otherwise just use
the fixed allocator type you like.

Michael Karcher

---
[ 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: apm35@student.open.ac.uk (apm)
Date: Fri, 23 Apr 2004 19:09:42 +0000 (UTC)
Raw View
Guys,

Here are two ways to for my class Foo to have a
vector of int using my custom allocator, apm_alloc:

class Foo {
std::vector<int, apm_alloc> myvec;
    :
    :
};

class Foo {
std::vector<int> myvec;
    :
    :
public:
Foo(const apm_alloc& myalloc = apm_alloc())
  : myvec(myalloc)
{
  // other ctor stuff
}

};

My question is: what do people think of the second form?
The first is more usual. Indeed I didn't even know about
the second way until a couple of days ago. Does the second
way use the allocator in the same was as the first way?
If so then it seems better to me because with the second
approach myvec is of type std::vector<int>, which means
I can pass myvec to any routine that takes a reference
to a std::vector<int> and it will be ok. When the
allocator forms part of the type there is a problem
passing a vector of one type to a routine that requires
a vector of a different type.

Regards,

Andrew Marlow

---
[ 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                       ]