Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

void* bloat with T& result

1 view
Skip to first unread message

p7er...@gmail.com

unread,
Jan 21, 2009, 1:57:14 PM1/21/09
to
Hi,

Let's say we have an

template <typename T>
class Array {
T& operator [] (int i) const;
};

I've tried to detect when T is a pointer and avoid bloat thru
Array<void*> but this does not seem to be allowed with static_cast for
the T& reference. For example, void* and T* may be different physical
sizes.

Any solutions?

Andy

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Balog Pal

unread,
Jan 21, 2009, 10:59:29 PM1/21/09
to
<p7er...@gmail.com>

> template <typename T>
> class Array {
> T& operator [] (int i) const;
> };
>
> I've tried to detect when T is a pointer and avoid bloat thru
> Array<void*> but this does not seem to be allowed with static_cast for
> the T& reference. For example, void* and T* may be different physical
> sizes.

Do you have any evidence that you get any real bloat?
Most functions should be inline and have the same code for most T*.
I'd check it out before attempting "optimisation" that would likely save
nothing or be worse.

And for possible solution: you create a proxy object that have a T* and
implicit operator T&. op[] returns this proxy inited via static_cast-ing the
pointer. (just typing here:)

Hak...@gmail.com

unread,
Jan 21, 2009, 10:57:35 PM1/21/09
to
On Jan 21, 1:57 pm, p7ere...@gmail.com wrote:

> template <typename T>
> class Array {
> T& operator [] (int i) const;
>
> };
>
> I've tried to detect when T is a pointer and avoid bloat thru
> Array<void*> but this does not seem to be allowed with static_cast for
> the T& reference. For example, void* and T* may be different physical
> sizes.
>
> Any solutions?

class BasicArray // Not templated.
{
// Define functions like resize,
// do stuff.
};

template< typename T >
class Array
{

enum { ELEM_SIZE = sizeof(T) };

BasicArray array; // Alternatively, derive.

// Redefine functions like derive to make sure they resize based
on type T's size.
// Do stuff.
};

However, if your code is excessively inlined...

template< typename T >
void Array::resize( size_t size )
{
BasicArray::resize( size * ELEM_SIZE );
}

this function might still be copied for every type (BasicArray::resize
is inlined, but Array::resize is not) or every instance of the call
(Array::resize is inlined; BasicArray::resize might inline also). To
really know whether you're making a difference, you should look at the
assembly, or ask someone else to if you cannot.

An alternative might be--although take this with a grain of salt; I do
not yet know much about this topic--use std::vector or boost::array,
but make it use an allocator to do what you want.

std::vector< int, std::allocator<void> > v; // Fails g++'s (boost's)
check that int and the alloc's type are the same.
std::vector< int, CustomAlloc<int> > v; // Might work.

Then, CustomAlloc can use the same technique I demonstrated above.
Looking at the g++ implementation, much of the non-type specific stuff
is handled by the allocator. Stuff like using the data (moving aside
from allocating, iterating, etc.) are not handled by the allocator and
the vector does little else.

I found this, which is pretty close to what I'm recommending:
http://74.125.95.132/search?q=cache:P5ID7_HRtJsJ:www.tantalon.com/pete/customallocators.ppt+stl+allocator<void>&hl=en&ct=clnk&cd=5&gl=us

A huge advantage to making your own allocator is that you can use this
allocator for any container, yours or a standard one, but a
disadvantage is if you want the internal pointers of your class to
void as well, which would force you to rewrite every container type.
But, honestly, I think this is such a trivial issue (the amount of
duplicate code you'll reduce mightn't be worth it) that I don't
recommend going to far with this. Especially if this is plain old
array class (Array<int> pretty much == int []) seeing as the code is
normally inlined (without the class) and small anyway.

Bo Persson

unread,
Jan 21, 2009, 10:55:47 PM1/21/09
to
p7er...@gmail.com wrote:
> Hi,
>
> Let's say we have an
>
> template <typename T>
> class Array {
> T& operator [] (int i) const;
> };
>
> I've tried to detect when T is a pointer and avoid bloat thru
> Array<void*> but this does not seem to be allowed with static_cast
> for the T& reference. For example, void* and T* may be different
> physical sizes.
>
> Any solutions?
>

Stop trying too hard, and trust your compiler. It will most likely do
it for you.


You should also consider if you really want to return a non-const
reference from a const Array.


Bo Persson

Seungbeom Kim

unread,
Jan 23, 2009, 6:19:32 AM1/23/09
to
p7er...@gmail.com wrote:
>
> Let's say we have an
>
> template <typename T>
> class Array {
> T& operator [] (int i) const;
> };
>
> I've tried to detect when T is a pointer and avoid bloat thru
> Array<void*> but this does not seem to be allowed with static_cast for
> the T& reference. For example, void* and T* may be different physical
> sizes.
>
> Any solutions?

I don't see why that's a problem. static_cast, or the implicit conversion
from T* to void* as well, will handle the conversion between void* and T*
correctly, even if their representations are different.

--
Seungbeom Kim

p7er...@gmail.com

unread,
Jan 23, 2009, 12:36:40 PM1/23/09
to
On Jan 21, 7:55 pm, "Bo Persson" <b...@gmb.dk> wrote:

> p7ere...@gmail.com wrote:
> > Hi,
>
> > Let's say we have an
>
> > template <typename T>
> > class Array {
> > T& operator [] (int i) const;
> > };
>
> > I've tried to detect when T is a pointer and avoid bloat thru
> > Array<void*> but this does not seem to be allowed with static_cast
> > for the T& reference. For example, void* and T* may be different
> > physical sizes.
>
> > Any solutions?
>
> Stop trying too hard, and trust your compiler. It will most likely do
> it for you.
>
> You should also consider if you really want to return a non-const
> reference from a const Array.

{ Edits: quoted sig & banner removed. -mod }

I have tons of Arrays (btree), FlatArrays, and Vectors based on this.
My own heap management that avoids stl. There is significant bloat,
especially with the Intel Compiler, GNU does better.

The point of my post is that if I return a T& in [] operator, then the
calling code can directly stamp the contents of the element
referenced. Lets say this T is MyObject*. And I attempt to avoid
bloat by using Array<void*> as a specialization with Array<MyObject*>
inlining all the calls. The problem is that I don't think I can do a:

...
void** p = GetElemPtr(i);
return static_cast<T&>(*p);

because sizeof(void*) may be different than sizeof(MyObject*)

joshua...@gmail.com

unread,
Jan 24, 2009, 2:15:37 PM1/24/09
to

First thing's first. I can't seem to find it in the standard where it
says all pointer-to-object types have the same size. It appears there
is no such guarantee. I'm having difficulty constructing an example
where this code violate a programmer's intuition to give bad results
without resorting to reinterpret_cast, so it may not be a real
problem. It just feels very wrong though. What reasons are there for
not mandating that all pointers-to-objects have the same size? Is
there really that much to be gained? Similar question, is there that
much to be gained by also not saying sizeof(bool) == 1?


Second, what bloat are you trying to reduce exactly? I see two bloats
you've mentioned, the bloat of the bad heap memory management which
you've replaced, and the bloat of Array<T> when T is a pointer type,
as intuitively a single implementation in terms of void pointer should
suffice.

Without template specialization, you're worried about the bloat caused
by Array<int*> and Array<float*>, as the compiler will instantiate
each class separately and presumably make separate functions and put
these in the code. You want to partially specialize the template for
pointer types. I do not see how this gets rid of the bloat; you will
still have a separate class instantiation for each template argument
used to instantiate Array. Sure, the specialized definition will be
implemented in terms of void pointers, but if you take the address of
one of the instantiated function templates, it is guaranteed to be
different than another one of the instantiated function templates,
i.e. there will still be the same code bloat.

Now, you could do this and factor the logic out into a small group of
non-template functions which act on void**, and arguably this could
cut down a little on bloat, but I suspect not all that much. (Of
course measure to see any effects.)

If the compiler / linker can do some whole program analysis, it could
see that you never take the address of any of these functions, and it
could combine the function implementations into the same function, as
the assembly for each different pointer-to-pointer types is probably
the same assembly. (Note that msvc has an option to turn on such
function combining \even if\ the program can tell the difference,
specifically two different functions could have the same address with
this option enabled.)

Moreover, could someone more knowledgeable comment on how inline would
affect this? Or common optimization techniques when code-inlining
small functions? How common is it for a compiler / linker to code-
inline small template functions like this? If these would be commonly
code-inlined, then there is no bloat to speak of. There are no
functions in the compiled object file. Instead, There would be just a
small amount of inlined code corresponding to each function call in
source, aka no bloat if you don't partially specialize Array for
pointer types.


Finally, you're speaking in very vague terms. If you could provide a
compilable code example demonstrating what you want to do, (or at
least a more complete code fragment), we might be better able to
help / explain.

Mathias Gaunard

unread,
Jan 24, 2009, 10:18:28 PM1/24/09
to
On 23 jan, 18:36, p7ere...@gmail.com wrote:

> void** p = GetElemPtr(i);
> return static_cast<T&>(*p);

You cannot cast a void* to a T&.

You probably meant
void* p = GetElemPtr(i);
return *static_cast<T*>(p);

Bart van Ingen Schenau

unread,
Jan 24, 2009, 10:15:51 PM1/24/09
to
p7er...@gmail.com wrote:

> On Jan 21, 7:55 pm, "Bo Persson" <b...@gmb.dk> wrote:
>>
>> You should also consider if you really want to return a non-const
>> reference from a const Array.
>
> { Edits: quoted sig & banner removed. -mod }
>
> I have tons of Arrays (btree), FlatArrays, and Vectors based on this.
> My own heap management that avoids stl. There is significant bloat,
> especially with the Intel Compiler, GNU does better.
>
> The point of my post is that if I return a T& in [] operator, then the
> calling code can directly stamp the contents of the element
> referenced.

That is fully understood. The question is, do you want this code to
compile without complaint, as it would with the example you posted:

void foo(const Array<int>& a)
{
// a is declared const here !!
a[42] = 0;
}

> Lets say this T is MyObject*. And I attempt to avoid
> bloat by using Array<void*> as a specialization with Array<MyObject*>
> inlining all the calls. The problem is that I don't think I can do a:
>
> ...
> void** p = GetElemPtr(i);
> return static_cast<T&>(*p);
>
> because sizeof(void*) may be different than sizeof(MyObject*)

How about doing it like this:
return *static_cast<T*>(*p);

Then the compiler will take care with any possible difference in size or
representation between void* and T*.
And unless you are playing very dirty tricks in the code of GetElemPtr,
the user of operator[] still gets a reference to the original object.

Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

Bo Persson

unread,
Jan 24, 2009, 10:21:58 PM1/24/09
to
p7er...@gmail.com wrote:
> On Jan 21, 7:55 pm, "Bo Persson" <b...@gmb.dk> wrote:
>> p7ere...@gmail.com wrote:
>>> Hi,
>>
>>> Let's say we have an
>>
>>> template <typename T>
>>> class Array {
>>> T& operator [] (int i) const;
>>> };
>>
>>> I've tried to detect when T is a pointer and avoid bloat thru
>>> Array<void*> but this does not seem to be allowed with static_cast
>>> for the T& reference. For example, void* and T* may be different
>>> physical sizes.
>>
>>> Any solutions?
>>
>> Stop trying too hard, and trust your compiler. It will most likely
>> do it for you.
>>
>> You should also consider if you really want to return a non-const
>> reference from a const Array.
>
>
> I have tons of Arrays (btree), FlatArrays, and Vectors based on
> this. My own heap management that avoids stl. There is significant
> bloat, especially with the Intel Compiler, GNU does better.

Sorry to hear that.

I use a compiler where, for example, std::vector member functions
generally share code for all types where sizeof(element_type) is the
same.

Is getting a new compiler an option? :-)

>
> The point of my post is that if I return a T& in [] operator, then
> the calling code can directly stamp the contents of the element
> referenced. Lets say this T is MyObject*. And I attempt to avoid
> bloat by using Array<void*> as a specialization with
> Array<MyObject*> inlining all the calls. The problem is that I
> don't think I can do a:
>
> ...
> void** p = GetElemPtr(i);
> return static_cast<T&>(*p);
>
> because sizeof(void*) may be different than sizeof(MyObject*)

That's a problem. On the other hand, I don't think you should return a
non-const reference anyway from a const member. If you specialize for
pointer-type members, why not just return the pointer?

template<class T>
class Array<T*>
{
T* operator[](int i) const;
};

Bo Persson

Erik Wikström

unread,
Jan 24, 2009, 10:48:41 PM1/24/09
to
On 2009-01-24 20:15, joshua...@gmail.com wrote:

> Similar question, is there that much to be gained by also not saying
> sizeof(bool) == 1?

By not defining the size of bool you allow the compiler to make the code
more efficient on architectures that can not efficiently work on units
smaller than a word (and the word is larger than a byte). By allowing
the compiler to use a whole word it can generate more efficient code,
whit the trade-off being that the program requires more memory.

--
Erik Wikström

Bo Persson

unread,
Jan 24, 2009, 10:49:15 PM1/24/09
to
joshua...@gmail.com wrote:
> On Jan 23, 9:36 am, p7ere...@gmail.com wrote:
>>
>>
>> ...
>> void** p = GetElemPtr(i);
>> return static_cast<T&>(*p);
>>
>> because sizeof(void*) may be different than sizeof(MyObject*)
>
> First thing's first. I can't seem to find it in the standard where
> it says all pointer-to-object types have the same size. It appears
> there is no such guarantee. I'm having difficulty constructing an
> example where this code violate a programmer's intuition to give
> bad results without resorting to reinterpret_cast, so it may not be
> a real problem. It just feels very wrong though. What reasons are
> there for not mandating that all pointers-to-objects have the same
> size? Is there really that much to be gained?

There are some word addressed machines where a string might pack
several chars into one word. In that case, a char* would be larger
than an int*. A void* would then have to be as large as the largest
pointer type.


> Similar question, is
> there that much to be gained by also not saying sizeof(bool) == 1?
>

There might be an advantage of allowing sizef(bool) == sizeof(int).


Bo Persson

Thomas Richter

unread,
Jan 24, 2009, 10:54:13 PM1/24/09
to
joshua...@gmail.com wrote:

>> void** p = GetElemPtr(i);
>> return static_cast<T&>(*p);
>>
>> because sizeof(void*) may be different than sizeof(MyObject*)
>
> First thing's first. I can't seem to find it in the standard where it
> says all pointer-to-object types have the same size.

No, but all of them have to be convertible forth and back to a void *,
which means that a void * must provide at least enough room for all of
them. That is, in principle an int * might be smaller than a void *. It
might actually be so on certain mainframes where for a char *, you also
need to record on which "subset" of a word you are pointing at, unlike
an int *, which points to the natural word-size of the machine.

> It appears there
> is no such guarantee. I'm having difficulty constructing an example
> where this code violate a programmer's intuition to give bad results
> without resorting to reinterpret_cast, so it may not be a real
> problem. It just feels very wrong though. What reasons are there for
> not mandating that all pointers-to-objects have the same size? Is
> there really that much to be gained?

See above, I would believe there are machines where sizeof(char *) >
sizeof(int *).

> Similar question, is there that
> much to be gained by also not saying sizeof(bool) == 1?

As part of a optimizing strategy, making a bool as large as an int might
provide benefits.

So long,
Thomas

Hak...@gmail.com

unread,
Jan 25, 2009, 6:05:37 PM1/25/09
to
On Jan 24, 10:21 pm, "Bo Persson" <b...@gmb.dk> wrote:

> I use a compiler where, for example, std::vector member functions
> generally share code for all types where sizeof(element_type) is the
> same.
>
> Is getting a new compiler an option? :-)

Are you saying that the library your compiler uses, that was written
for it, does this, or the compiler itself? Might the library be
portable to other compilers? (Wishful thinking, I know.)

Martin Bonner

unread,
Jan 26, 2009, 5:57:13 PM1/26/09
to
On Jan 25, 3:18 am, Mathias Gaunard <loufo...@gmail.com> wrote:
> On 23 jan, 18:36, p7ere...@gmail.com wrote:
>
> > void** p = GetElemPtr(i);
> > return static_cast<T&>(*p);
>
> You cannot cast a void* to a T&.

He doesn't. He casts *p to a T&, not p.


>
> You probably meant
> void* p = GetElemPtr(i);
> return *static_cast<T*>(p);

That works.

Bo Persson

unread,
Jan 27, 2009, 1:19:33 AM1/27/09
to
Hak...@gmail.com wrote:
> On Jan 24, 10:21 pm, "Bo Persson" <b...@gmb.dk> wrote:
>
>> I use a compiler where, for example, std::vector member functions
>> generally share code for all types where sizeof(element_type) is
>> the same.
>>
>> Is getting a new compiler an option? :-)
>
> Are you saying that the library your compiler uses, that was written
> for it, does this, or the compiler itself? Might the library be
> portable to other compilers? (Wishful thinking, I know.)

It's a standard Standard library (one of the few there is :-).

The linker tries to remove duplicate instantiations of the same
template by discarding identical blocks of code. As a byproduct, it
most of the time removes all but one copy of each function for
identically sized objects. By accident really!

MSVC, by the way.

Bo Persson

p7er...@gmail.com

unread,
Jan 27, 2009, 1:27:34 AM1/27/09
to
>
> He doesn't. He casts *p to a T&, not p.
>
>
>
> > You probably meant
> > void* p = GetElemPtr(i);
> > return *static_cast<T*>(p);
>
> That works.

Hi, I saw this attempt and assumed it does not work. Can you explain
how it does if void* and T* are different sizes? E.g. the users of
the Array<T> class will always get a T& and play with it directly,
circumventing any additional static_casts.

Yechezkel Mett

unread,
Jan 27, 2009, 10:19:11 AM1/27/09
to
On Jan 27, 8:19 am, "Bo Persson" <b...@gmb.dk> wrote:
> Hak...@gmail.com wrote:
> > On Jan 24, 10:21 pm, "Bo Persson" <b...@gmb.dk> wrote:
>
> >> I use a compiler where, for example, std::vector member functions
> >> generally share code for all types where sizeof(element_type) is
> >> the same.
...

>
> The linker tries to remove duplicate instantiations of the same
> template by discarding identical blocks of code. As a byproduct, it
> most of the time removes all but one copy of each function for
> identically sized objects. By accident really!
>
> MSVC, by the way.

Not exactly. The linker removes duplicate instantiations of the same
template by using COMDAT sections. This does _not_ discard identical
blocks of code, only items with the same name (i.e. the same
instantiation of the same template).
The feature you mention (of removing binary-identical sections) is
called identical COMDAT folding (ICF) which is enabled separately in
the linker (it's enabled by default in release mode). Even when ICF is
disabled, duplicate template instantiations will be removed.

And now for the on-topic note: This option is not actually conforming,
because it can give multiple functions the same address if they happen
to compile to identical code (which is common in template functions,
as mentioned).

Yechezkel Mett

Bart van Ingen Schenau

unread,
Jan 27, 2009, 6:22:13 PM1/27/09
to
p7er...@gmail.com wrote:

>>
>> He doesn't. He casts *p to a T&, not p.
>>
>>
>>
>> > You probably meant
>> > void* p = GetElemPtr(i);
>> > return *static_cast<T*>(p);
>>
>> That works.
>
> Hi, I saw this attempt and assumed it does not work. Can you explain
> how it does if void* and T* are different sizes? E.g. the users of
> the Array<T> class will always get a T& and play with it directly,
> circumventing any additional static_casts.

It works because the standard mandates that it works.
The standard requires that for any object type T, the conversion
sequence T* -> void* -> T* must result in the original pointer value.
This in effect means that void* must be the largest pointer type used
by the implementation.

When you instantiate the type Array<T>, the compiler knows which type T
represents, so it also knows the corresponding type T* and can generate
whatever code is needed to adjust a void* to a T*. This can be as
simple as an identity conversion (no-op),or something like a shift on
the numerical value of the pointer. For the majority of platforms, this
is likely to be a no-op.

Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Francis Glassborow

unread,
Jan 29, 2009, 2:53:53 AM1/29/09
to
Bart van Ingen Schenau wrote:
> p7er...@gmail.com wrote:
>
>>> He doesn't. He casts *p to a T&, not p.
>>>
>>>
>>>
>>>> You probably meant
>>>> void* p = GetElemPtr(i);
>>>> return *static_cast<T*>(p);
>>> That works.
>> Hi, I saw this attempt and assumed it does not work. Can you explain
>> how it does if void* and T* are different sizes? E.g. the users of
>> the Array<T> class will always get a T& and play with it directly,
>> circumventing any additional static_casts.
>
> It works because the standard mandates that it works.
> The standard requires that for any object type T, the conversion
> sequence T* -> void* -> T* must result in the original pointer value.
> This in effect means that void* must be the largest pointer type used
> by the implementation.
>
Must is too strong. In practice it will be so but it is quite possible to
design an implementation in which it is not true. There is no requirement
that void* encode anything other than the 'address' in a way that it can
restore it. There is no requirement that a T* only encode the 'address'
it could also include (redundant) type information.

--
Note that robinton.demon.co.uk addresses are no longer valid.

Mathias Gaunard

unread,
Jan 29, 2009, 4:15:06 PM1/29/09
to
On 26 jan, 23:57, Martin Bonner <martinfro...@yahoo.co.uk> wrote:
> On Jan 25, 3:18 am, Mathias Gaunard <loufo...@gmail.com> wrote:
>
> > On 23 jan, 18:36, p7ere...@gmail.com wrote:
>
> > > void** p = GetElemPtr(i);
> > > return static_cast<T&>(*p);
>
> > You cannot cast a void* to a T&.
>
> He doesn't. He casts *p to a T&, not p.

p being a void**, *p is a void*.
So he does cast a void* to a T&, which of course shouldn't even
compile.

Bart van Ingen Schenau

unread,
Jan 29, 2009, 10:39:23 PM1/29/09
to
Francis Glassborow wrote:

> Bart van Ingen Schenau wrote:
>> p7er...@gmail.com wrote:
>>
>>>> He doesn't. He casts *p to a T&, not p.
>>>>
>>>>
>>>>
>>>>> You probably meant
>>>>> void* p = GetElemPtr(i);
>>>>> return *static_cast<T*>(p);
>>>> That works.
>>> Hi, I saw this attempt and assumed it does not work. Can you
>>> explain
>>> how it does if void* and T* are different sizes? E.g. the users of
>>> the Array<T> class will always get a T& and play with it directly,
>>> circumventing any additional static_casts.
>>
>> It works because the standard mandates that it works.
>> The standard requires that for any object type T, the conversion
>> sequence T* -> void* -> T* must result in the original pointer value.
>> This in effect means that void* must be the largest pointer type used
>> by the implementation.
>>
> Must is too strong. In practice it will be so but it is quite possible
> to design an implementation in which it is not true. There is no
> requirement that void* encode anything other than the 'address' in a
> way that it can restore it. There is no requirement that a T* only
> encode the 'address' it could also include (redundant) type
> information.

Well, according to [expr.static.cast]/10 (5.2.9/10 in C++03), if a T*
encodes information that is not preserved after a conversion to void*
and back, that information is not part of the pointer's value:
<quote>
A value of type pointer to object converted to "pointer to cv void" and
back to the original pointer type will have its original value.
</quote>

So, I don't see how my 'must' is too strong if I just state what the
standard asserts.

Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Greg Herlihy

unread,
Jan 29, 2009, 10:42:32 PM1/29/09
to
On Jan 29, 1:15 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
> On 26 jan, 23:57, Martin Bonner <martinfro...@yahoo.co.uk> wrote:
>
> > On Jan 25, 3:18 am, Mathias Gaunard <loufo...@gmail.com> wrote:
>
> > > On 23 jan, 18:36, p7ere...@gmail.com wrote:
>
> > > > void** p = GetElemPtr(i);
> > > > return static_cast<T&>(*p);
>
> > > You cannot cast a void* to a T&.
>
> > He doesn't. He casts *p to a T&, not p.
>
> p being a void**, *p is a void*.
> So he does cast a void* to a T&, which of course shouldn't even
> compile.

The whole point of this code is to optimize the case where T is a
pointer type. Therefore the code is casting the "void *" variable, p,
to a "T2*" reference (with T2 being whatever type T points to). So not
only will this cast compile successfully - it will also work correctly
when executed.

After all, this code does does not have to assume that sizeof(void*)
is equal to sizeof(T) or sizeof(T2*). Instead, this routine only has
to assume that sizeof(void*) is at least as large as any other pointer
type. And because a C++ program must be able to "round-trip" any
pointer type through a void pointer type - we can conclude that a void
pointer type must be at least as large as any other pointer type in
order to preserve and restore the original pointer's information.

Of course, a program should nonetheless verify its assumptions - in
this case, I would use some kind of static_assert:

static_assert< not std::tr1::is_pointer<T>::value or sizeof<T> <=
sizeof(void*),
"void pointer is smaller than instantiated pointer type">

Greg

p7er...@gmail.com

unread,
Jan 29, 2009, 10:43:25 PM1/29/09
to
On Jan 29, 1:15 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
> On 26 jan, 23:57, Martin Bonner <martinfro...@yahoo.co.uk> wrote:
>
> > On Jan 25, 3:18 am, Mathias Gaunard <loufo...@gmail.com> wrote:
>
> > > On 23 jan, 18:36, p7ere...@gmail.com wrote:
>
> > > > void** p = GetElemPtr(i);
> > > > return static_cast<T&>(*p);
>
> > > You cannot cast a void* to a T&.
>
> > He doesn't. He casts *p to a T&, not p.
>
> p being a void**, *p is a void*.
> So he does cast a void* to a T&, which of course shouldn't even
> compile.
>

Will it not even compile if T is really a MyObject* as determined by
specialization?

Alf P. Steinbach

unread,
Jan 30, 2009, 1:38:40 PM1/30/09
to
* Greg Herlihy:

> On Jan 29, 1:15 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
>> On 26 jan, 23:57, Martin Bonner <martinfro...@yahoo.co.uk> wrote:
>>
>>> On Jan 25, 3:18 am, Mathias Gaunard <loufo...@gmail.com> wrote:
>>>> On 23 jan, 18:36, p7ere...@gmail.com wrote:
>>>>> void** p = GetElemPtr(i);
>>>>> return static_cast<T&>(*p);
>>>> You cannot cast a void* to a T&.
>>> He doesn't. He casts *p to a T&, not p.
>> p being a void**, *p is a void*.
>> So he does cast a void* to a T&, which of course shouldn't even
>> compile.
>
> The whole point of this code is to optimize the case where T is a
> pointer type. Therefore the code is casting the "void *" variable, p,
> to a "T2*" reference (with T2 being whatever type T points to). So not
> only will this cast compile successfully - it will also work correctly
> when executed.
>
> After all, this code does does not have to assume that sizeof(void*)
> is equal to sizeof(T) or sizeof(T2*).

Since it's casting to a reference it needs to know that both the size and the
bits are appropriate for T2*.

It would be different if it casted to pointer value.

But anyways, consider

<code>
int main()
{
void* p;
int*& r = static_cast<int*&>( p );
}
</code>

<compilation>
C:\temp> gnuc x.cpp
x.cpp: In function `int main()':
x.cpp:4: error: invalid static_cast from type `void*' to type `int*&'
x.cpp:4: warning: unused variable 'r'

C:\temp> msvc x.cpp
x.cpp
x.cpp(4) : error C2440: 'static_cast' : cannot convert from 'void *' to 'int *& '
static_cast to reference can only be used for valid initializations or
for lvalue casts between related classes

C:\temp> _
</compilation>


Cheers, & hth.,

- Alf

Francis Glassborow

unread,
Feb 1, 2009, 11:25:08 AM2/1/09
to
Greg Herlihy wrote:
> On Jan 29, 1:15 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
>> On 26 jan, 23:57, Martin Bonner <martinfro...@yahoo.co.uk> wrote:
>>
>>> On Jan 25, 3:18 am, Mathias Gaunard <loufo...@gmail.com> wrote:
>>>> On 23 jan, 18:36, p7ere...@gmail.com wrote:
>>>>> void** p = GetElemPtr(i);
>>>>> return static_cast<T&>(*p);
>>>> You cannot cast a void* to a T&.
>>> He doesn't. He casts *p to a T&, not p.
>> p being a void**, *p is a void*.
>> So he does cast a void* to a T&, which of course shouldn't even
>> compile.
>
> The whole point of this code is to optimize the case where T is a
> pointer type. Therefore the code is casting the "void *" variable, p,
> to a "T2*" reference (with T2 being whatever type T points to). So not
> only will this cast compile successfully - it will also work correctly
> when executed.
>
> After all, this code does does not have to assume that sizeof(void*)
> is equal to sizeof(T) or sizeof(T2*). Instead, this routine only has
> to assume that sizeof(void*) is at least as large as any other pointer
> type. And because a C++ program must be able to "round-trip" any
> pointer type through a void pointer type - we can conclude that a void
> pointer type must be at least as large as any other pointer type in
> order to preserve and restore the original pointer's information.

False deduction, see my post elsethread.

>
> Of course, a program should nonetheless verify its assumptions - in
> this case, I would use some kind of static_assert:
>
> static_assert< not std::tr1::is_pointer<T>::value or sizeof<T> <=
> sizeof(void*),
> "void pointer is smaller than instantiated pointer type">
>

Good, I am glad you put that last bit in because the round trip
requirement does not require it even if it is true on every
implementation I have ever used.


--
Note that robinton.demon.co.uk addresses are no longer valid.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

0 new messages