Topic: allocator rebinding and STL container specializations


Author: Martin Sebor <marts@att.net>
Date: 1999/08/18
Raw View
"Siemel B. Naran" wrote:
>
> On 16 Aug 99 08:37:05 GMT, Martin Sebor <marts@att.net> wrote:
>
> >Q1) Is this implementation of std::vector legal? Note the rebind to T
> >which makes things like std::vector<int, std::allocator<double>> compile.
> >
> >template <class T, class Allocator = allocator<T> >
> >class vector
> >{
> >public:
> >    typedef typename Allocator::template rebind <T>::other
> >_T_alloc_type;
> >    ...
> >    typedef typename _T_alloc_type::pointer   pointer;
> >    typedef typename _T_alloc_type::reference   reference;
>
> This is reasonable but it is not legal because it will break code if you
> move to a compiler+library that does not support the convenience.  For
> example, the following program would compile on your hypothetical
> implementation
>    std::vector<int,std::allocator<double> > v;
> But this code would fail on another compiler.
>

This would just make the client code unportable, but does it necessarily
make the implementation non-conforming? The standard does declare
pointer, reference, etc., like so, though:

template <class T, class Allocator = allocator <T> >
class vector
{
public:
    ...
    typedef typename Allocator::reference reference;
};


> Your idea seems reasonable because the class may want to rebind the
> Allocator to a Allocator<Node>, where Node is a nested private class of
> the container, for example a container like list or map or set.

Yes, but rebinding the user-supplied allocator to another may have the
sideffect of not using the allocator to allocate or construct the
container's elements at all.

> >Q3) Is it possible to implement a _standard-conforming_ partial
> >specialization for vector<T*> in terms of an explicitly instantiated
> >specialization std::vector<void*, std::allocator<void*> >? I.e.,
> >something
> >along the lines of
> >
> >namespace std {
> >
> >template class vector<void*, allocator<void*> >;
> >
> >template <class T>
> >class vector<T*, allocator<T*> > {
> >public:
> >    typedef typename allocator<T*>::pointer   pointer;
> >    ...
> >    typedef typename allocator<T*>::template rebind<void*>::other
> >__imp_alloc_type;
> >    // implement in terms of std::vector<void*, __imp_alloc_type >
> >};
> >
> >}
>
> I think this is possible.  The rebinding here is a private implementation
> detail of the class, and code that uses std::vector<Thing*> compiles on
> any compiler+library.
>

Again, the rebinding will almost certainly have the effect of bypassing
the user-supplied allocator and using the rebound one instead. This
won't be a problem in 99% of all cases but that 1% bothers me. Consider

// user-defined explict specialization, allocate in private heap X
template <> class allocator<int> { /* ... */ };
template <> class allocator<int*> { /* ... */ };

// create vector of ints, elements stored in heap X
vector <int> vi;

// oops, elements allocated using some allocator<void*>...
vector <int*> v;

My feeling is that this in itself disqualifies such an implementation
from conforming to the standard.

>
> >Q4) If the above is legal, how about
> >
> >template <class T, template <class U> class Allocator>
> >class vector<T*, Allocator<T*> > {
> >public:
> >    typedef typename Allocator<T*>::pointer   pointer;
> >    ...
> >    typedef typename Allocator<T*>::template rebind<void*>::other
> >__imp_alloc_type;
> >    // implement in terms of std::vector<void*, __imp_alloc_type >
> >};
>
> This is not legal.  The Allocator template argument cannot be a template
> template argument.  One reason for this is that if we used template
> template arguments, we are limited in the number of template arguments
> we can use.  It is reasonable to want to write an allocator like this:
>    template <class T, size_t N> class myallocator { ... };
> But we can't say
>    std::vector<int,myallocator> v;
> because myallocator takes two template arguments whereas class vector
> expects Allocator to take one template argument.
>
> Still, I think that it is cleaner design for Allocator to be a template
> template argument.  But it is too late to change the standard.
>

This is a partial specialization of the primary template, and the
non-templatized allocator will not be a match here and will be handled
by the primary template (or an appropriate specialization).

The idea here is a generalization of Q3) with the constraint that the
allocator template parameter itself be a template.

In my opinion this doesn't conform to the standard either.

>
> >Q5) If it is, then how about
> >
> >template <class T, class Allocator>
> >class vector<T*, Allocator> {
> >public:
> >    typedef typename Allocator::pointer   pointer;
> >    ...
> >    typedef typename Allocator::template rebind<void*>::other
> >__imp_alloc_type;
> >    // implement in terms of std::vector<void*, __imp_alloc_type >
> >};
>
> If Q4) is legal, then Q5) is legal too.
> But Q4) isn't legal.
>

Right. So it looks like it isn't possible to implement a
standard-conforming partial specialization of the STL containers for
void*. Bummer.

---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: 1999/08/18
Raw View
Matt Austern wrote:
>
> "James Kuyper Jr." <kuyper@wizard.net> writes:
....
> > >    std::vector<int,std::allocator<double> > v;
> > > But this code would fail on another compiler.
> >
> > Is it guaranteed to work by the standard? An implementation of vector<>
> > that conforms to 23.4.2 will have a vector<>::reference whose value_type
> > doesn't meet vector's container requirements.
>
> According to the standard (23.1/8), the allocator's value type must be
> the same as the container's value type. If it isn't, then (17.1.15)
> the behavior is undefined.

Thanks - I knew that was true, but I couldn't remember where it was said
in the standard.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Martin Sebor <marts@att.net>
Date: 1999/08/16
Raw View
Q1) Is this implementation of std::vector legal? Note the rebind to T
which
makes things like std::vector<int, std::allocator<double>> compile.

template <class T, class Allocator = allocator<T> >
class vector
{
public:
    typedef typename Allocator::template rebind <T>::other
_T_alloc_type;
    ...
    typedef typename _T_alloc_type::pointer   pointer;
    typedef typename _T_alloc_type::reference   reference;
    ...
};


Q2) If it is, what should the allocator_type typedef look like?

    typedef Allocator   allocator_type;

or

    typedef _T_alloc_type   allocator_type;


Q3) Is it possible to implement a _standard-conforming_ partial
specialization for vector<T*> in terms of an explicitly instantiated
specialization std::vector<void*, std::allocator<void*> >? I.e.,
something
along the lines of

namespace std {

template class vector<void*, allocator<void*> >;

template <class T>
class vector<T*, allocator<T*> > {
public:
    typedef typename allocator<T*>::pointer   pointer;
    ...
    typedef typename allocator<T*>::template rebind<void*>::other
__imp_alloc_type;
    // implement in terms of std::vector<void*, __imp_alloc_type >
};

}

Note: Only std::vector<T*, std::allocator<T*> > instantiations (other
than
those explicitly specialized) for all T handled.


Q4) If the above is legal, how about

template <class T, template <class U> class Allocator>
class vector<T*, Allocator<T*> > {
public:
    typedef typename Allocator<T*>::pointer   pointer;
    ...
    typedef typename Allocator<T*>::template rebind<void*>::other
__imp_alloc_type;
    // implement in terms of std::vector<void*, __imp_alloc_type >
};

Note: All std::vector<T*, A<T*> > instantiations (other than those
explicitly specialized) for all T and A<T*>, where A is an
std::allocator-compatible template class taking one parameter, handled.


Q5) If it is, then how about

template <class T, class Allocator>
class vector<T*, Allocator> {
public:
    typedef typename Allocator::pointer   pointer;
    ...
    typedef typename Allocator::template rebind<void*>::other
__imp_alloc_type;
    // implement in terms of std::vector<void*, __imp_alloc_type >
};

Note: All std::vector<T*, A> instantiations (other than those explicitly
specialized) for all T and A, where A is any std::allocator-compatible
class, handled.

Martin
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/08/16
Raw View
On 16 Aug 99 08:37:05 GMT, Martin Sebor <marts@att.net> wrote:

>Q1) Is this implementation of std::vector legal? Note the rebind to T
>which makes things like std::vector<int, std::allocator<double>> compile.
>
>template <class T, class Allocator = allocator<T> >
>class vector
>{
>public:
>    typedef typename Allocator::template rebind <T>::other
>_T_alloc_type;
>    ...
>    typedef typename _T_alloc_type::pointer   pointer;
>    typedef typename _T_alloc_type::reference   reference;

This is reasonable but it is not legal because it will break code if you
move to a compiler+library that does not support the convenience.  For
example, the following program would compile on your hypothetical
implementation
   std::vector<int,std::allocator<double> > v;
But this code would fail on another compiler.

Your idea seems reasonable because the class may want to rebind the
Allocator to a Allocator<Node>, where Node is a nested private class of
the container, for example a container like list or map or set.



>Q2) If it is, what should the allocator_type typedef look like?
>
>    typedef Allocator   allocator_type;
>
>or
>
>    typedef _T_alloc_type   allocator_type;

I don't know.  The issue is that '_T_alloc_type' may depend on private
nested classes.  Or '_T_alloc_type' may be defined in an unnamed in the
pp file, in which case it isn't even available in the header file.  So
I'd say that the first way is valid.




>Q3) Is it possible to implement a _standard-conforming_ partial
>specialization for vector<T*> in terms of an explicitly instantiated
>specialization std::vector<void*, std::allocator<void*> >? I.e.,
>something
>along the lines of
>
>namespace std {
>
>template class vector<void*, allocator<void*> >;
>
>template <class T>
>class vector<T*, allocator<T*> > {
>public:
>    typedef typename allocator<T*>::pointer   pointer;
>    ...
>    typedef typename allocator<T*>::template rebind<void*>::other
>__imp_alloc_type;
>    // implement in terms of std::vector<void*, __imp_alloc_type >
>};
>
>}

I think this is possible.  The rebinding here is a private implementation
detail of the class, and code that uses std::vector<Thing*> compiles on
any compiler+library.

>Note: Only std::vector<T*, std::allocator<T*> > instantiations (other
>than
>those explicitly specialized) for all T handled.





>Q4) If the above is legal, how about
>
>template <class T, template <class U> class Allocator>
>class vector<T*, Allocator<T*> > {
>public:
>    typedef typename Allocator<T*>::pointer   pointer;
>    ...
>    typedef typename Allocator<T*>::template rebind<void*>::other
>__imp_alloc_type;
>    // implement in terms of std::vector<void*, __imp_alloc_type >
>};

This is not legal.  The Allocator template argument cannot be a template
template argument.  One reason for this is that if we used template
template arguments, we are limited in the number of template arguments
we can use.  It is reasonable to want to write an allocator like this:
   template <class T, size_t N> class myallocator { ... };
But we can't say
   std::vector<int,myallocator> v;
because myallocator takes two template arguments whereas class vector
expects Allocator to take one template argument.

Still, I think that it is cleaner design for Allocator to be a template
template argument.  But it is too late to change the standard.

>Note: All std::vector<T*, A<T*> > instantiations (other than those
>explicitly specialized) for all T and A<T*>, where A is an
>std::allocator-compatible template class taking one parameter, handled.




>Q5) If it is, then how about
>
>template <class T, class Allocator>
>class vector<T*, Allocator> {
>public:
>    typedef typename Allocator::pointer   pointer;
>    ...
>    typedef typename Allocator::template rebind<void*>::other
>__imp_alloc_type;
>    // implement in terms of std::vector<void*, __imp_alloc_type >
>};

If Q4) is legal, then Q5) is legal too.
But Q4) isn't legal.

>Note: All std::vector<T*, A> instantiations (other than those explicitly
>specialized) for all T and A, where A is any std::allocator-compatible
>class, handled.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: 1999/08/17
Raw View
"Siemel B. Naran" wrote:
>
> On 16 Aug 99 08:37:05 GMT, Martin Sebor <marts@att.net> wrote:
>
> >Q1) Is this implementation of std::vector legal? Note the rebind to T
> >which makes things like std::vector<int, std::allocator<double>> compile.
> >
> >template <class T, class Allocator = allocator<T> >
> >class vector
> >{
> >public:
> >    typedef typename Allocator::template rebind <T>::other
> >_T_alloc_type;
> >    ...
> >    typedef typename _T_alloc_type::pointer   pointer;
> >    typedef typename _T_alloc_type::reference   reference;
>
> This is reasonable but it is not legal because it will break code if you
> move to a compiler+library that does not support the convenience.  For
> example, the following program would compile on your hypothetical
> implementation
>    std::vector<int,std::allocator<double> > v;
> But this code would fail on another compiler.

Is it guaranteed to work by the standard? An implementation of vector<>
that conforms to 23.4.2 will have a vector<>::reference whose value_type
doesn't meet vector's container requirements.

....
> >Q4) If the above is legal, how about
> >
> >template <class T, template <class U> class Allocator>
> >class vector<T*, Allocator<T*> > {
> >public:
> >    typedef typename Allocator<T*>::pointer   pointer;
> >    ...
> >    typedef typename Allocator<T*>::template rebind<void*>::other
> >__imp_alloc_type;
> >    // implement in terms of std::vector<void*, __imp_alloc_type >
> >};
>
> This is not legal.  The Allocator template argument cannot be a template
> template argument.  One reason for this is that if we used template

Look more closely. This is a partial specialization pf vector<>, not the
template definition itself. Allocator is a parameter of the
specialization, but it's not a parameter of the vector<> template. The
template argument of vector<> is Allocator<T*>. This means that
'Allocator' means something different in this specialization than the
parameter of the same name used in the standard, but the names used for
template parameters in the standard are examples, not normative text.
Actually, I think that standard library template parameters must
necessarily be implemented using reserved names, to avoid clashes with,
for example, macros in user code that preceeds the standard headers. As
long as every reference to Allocator in the standard is replaced with
Allocator<T*> in this specialization, implementation in this fashion is
feasible.

I'd recommend re-naming the parameter though, to reduce confusion.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Matt Austern <austern@sgi.com>
Date: 1999/08/17
Raw View
"James Kuyper Jr." <kuyper@wizard.net> writes:

> > >Q1) Is this implementation of std::vector legal? Note the rebind to T
> > >which makes things like std::vector<int, std::allocator<double>> compile.
> > >
> > >template <class T, class Allocator = allocator<T> >
> > >class vector
> > >{
> > >public:
> > >    typedef typename Allocator::template rebind <T>::other
> > >_T_alloc_type;
> > >    ...
> > >    typedef typename _T_alloc_type::pointer   pointer;
> > >    typedef typename _T_alloc_type::reference   reference;
> >
> > This is reasonable but it is not legal because it will break code if you
> > move to a compiler+library that does not support the convenience.  For
> > example, the following program would compile on your hypothetical
> > implementation
> >    std::vector<int,std::allocator<double> > v;
> > But this code would fail on another compiler.
>
> Is it guaranteed to work by the standard? An implementation of vector<>
> that conforms to 23.4.2 will have a vector<>::reference whose value_type
> doesn't meet vector's container requirements.

According to the standard (23.1/8), the allocator's value type must be
the same as the container's value type.  If it isn't, then (17.1.15)
the behavior is undefined.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/08/17
Raw View
On 17 Aug 99 03:54:40 GMT, James Kuyper Jr. <kuyper@wizard.net> wrote:

>Is it guaranteed to work by the standard? An implementation of vector<>
>that conforms to 23.4.2 will have a vector<>::reference whose value_type
>doesn't meet vector's container requirements.

Why does an implementation have to do this
   typedef typename allocator<T>::reference reference;
Why can't an implementation do this
   typedef T& reference; // version2
If an implementation did version2, then it wouldn't matter what allocator
we used!  Which makes sense, because we might want have to rebind the
allocator from allocator<T> to allocator<Node<T>>.

For that matter, why does the implementation have to define 'reference' at
all?  As we have the typedef 'value_type', we can always create type
'reference' on demand as 'value_type&'.

For that matter, why can't we use
   using allocator<T>::reference;
if we derived from the class allocator.  Why does the standard have these
silly rules anyway?


>> >template <class T, template <class U> class Allocator>
>> >class vector<T*, Allocator<T*> > {

>> This is not legal.  The Allocator template argument cannot be a template
>> template argument.  One reason for this is that if we used template

>Look more closely. This is a partial specialization pf vector<>, not the
>template definition itself. Allocator is a parameter of the

I missed that.  Anyway, I still don't think it is legal although it is
reasonable.  If you provided the above specialization, then people who
use your implementation would write code that uses the specialization
-- for example, std::vector<Animal*,std::allocator> -- and this code
wouldn't work when they moved to a conforming compiler+library.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1999/08/17
Raw View
"Siemel B. Naran" wrote:
>
> On 17 Aug 99 03:54:40 GMT, James Kuyper Jr. <kuyper@wizard.net> wrote:
>
> >Is it guaranteed to work by the standard? An implementation of vector<>
> >that conforms to 23.4.2 will have a vector<>::reference whose value_type
> >doesn't meet vector's container requirements.
>
> Why does an implementation have to do this
>    typedef typename allocator<T>::reference reference;

Because it says so in 23.4.2?

> For that matter, why does the implementation have to define 'reference' at
> all? ...

Because it says so in the container requirements?

> if we derived from the class allocator.  Why does the standard have these
> silly rules anyway?

Ah! I was talking about what the standard says is legal for
implementors; you're talking about how a standard library class could
have been designed differently. Well, if it doesn't count as a defect,
it won't be changed in less than 10 years. By then there will be a large
body of code that depends on the way it's currently designed, inhibiting
further changes. I'm not going to worry about it.

> >> >template <class T, template <class U> class Allocator>
> >> >class vector<T*, Allocator<T*> > {
>
> >> This is not legal.  The Allocator template argument cannot be a template
> >> template argument.  One reason for this is that if we used template
>
> >Look more closely. This is a partial specialization pf vector<>, not the
> >template definition itself. Allocator is a parameter of the
>
> I missed that.  Anyway, I still don't think it is legal although it is
> reasonable.  If you provided the above specialization, then people who
> use your implementation would write code that uses the specialization
> -- for example, std::vector<Animal*,std::allocator> -- and this code

But that code wouldn't invoke the specialization, because it doesn't
match the specialization's pattern. The first argument unambiguously
leads to the deduction that T==Animal, therefore the second argument
must be Allocator<Animal*>, for some value of Allocator. There's no
value of Allocator that makes std::allocator match that pattern.

To use the specialization, you must give the parent template arguments
that match the specialization. In this case, the specialization would be
instantiated for

 std::vector<Animal*, std::allocator<Animal*> >

which matches the the specialization for T==Animal, and
Allocator==std::allocator. Since std::allocator<T*> is the default
second argument for vector<>, I believe that

 std::vector<Animal*>

would also invoke the specialization, though I'd need to check my copy
of the standard (which is at home) to be sure.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]