auto_ptr and operator void*();

M

Marcin Vorbrodt

Is there any reason why auto_ptr does not have the cast operator like this:

operator void* ();

So that one could easily test auto_ptr for NULL ???
Standard does not declare such operator.
Is the only way to check if auto_ptr points to NULL, is to use .get() method
?

Martin
 
W

WW

Marcin said:
Is there any reason why auto_ptr does not have the cast operator like
this:

operator void* ();

So that one could easily test auto_ptr for NULL ???
Standard does not declare such operator.
Is the only way to check if auto_ptr points to NULL, is to use .get()
method ?

Nope. There is another one, but that is uglier. To call opertator-> in its
function form. I dunno what is the rationale to leave out operator void*...
It might be present in the rationale in the upcoming C++ Standard book. But
until then I guess the best is to ask in comp.lang.c++.moderated. Guys who
actually worked on this design mostly hang out there because they gave up on
this group due to the sheer amount of trolls and off-topic posts. IIRC I
have read about how auto_ptr became what it is today, but I do not recall
talling anything about the "missing" operator bool or void*.

According to Herb Sutter in http://www.gotw.ca/gotw/025.htm it was Bill
Gibbons, Greg Colvin and Steve Rumsby who has finalized auto_ptr.
 
D

David B. Held

Marcin Vorbrodt said:
Is there any reason why auto_ptr does not have the cast
operator like this:

operator void* ();

So that one could easily test auto_ptr for NULL ???

This would allow someone to write:

auto_ptr<int> p(new int);

delete p;
Standard does not declare such operator.

Nor should it. A "safe bool" idiom would be better.
Is the only way to check if auto_ptr points to NULL, is to
use .get() method ?

As far as I know, but that's not a guarantee that there isn't
another way.

Dave
 
K

Kevin Goodsell

David said:
This would allow someone to write:

auto_ptr<int> p(new int);

delete p;

Would it? Can you delete a void *? The delete operator wouldn't know
what destructor (if any) to use, or the amount of memory to be freed
(unless it's stored with the allocated memory somehow).

-Kevin
 
W

WW

Kevin said:
Would it? Can you delete a void *? The delete operator wouldn't know
what destructor (if any) to use, or the amount of memory to be freed
(unless it's stored with the allocated memory somehow).

According to 5.3.5 P 1, 2 not.

"In the first alternative (delete object), the value of the operand of
delete shall be a pointer to a nonarray object or a pointer to a subobject
(1.8) representing a base class of such an object (clause 10). If not, the
behavior is undefined."
 
K

Kevin Goodsell

WW said:
According to 5.3.5 P 1, 2 not.

"In the first alternative (delete object), the value of the operand of
delete shall be a pointer to a nonarray object or a pointer to a subobject
(1.8) representing a base class of such an object (clause 10). If not, the
behavior is undefined."

So are you saying it can be a void *, or it cannot? As far as I can
tell, this passage only talks about the value of the operand, not the type.

-Kevin
 
K

Kevin Goodsell

Kevin said:
So are you saying it can be a void *, or it cannot? As far as I can
tell, this passage only talks about the value of the operand, not the type.

From the same section, paragraph 3:

3 In the first alternative (delete object), if the static type of the
operand is different from its dynamic type, the static type shall be a
base class of the operand's dynamic type and the static type shall
have a virtual destructor or the behavior is undefined.

I think that would cover void *, meaning the behavior is undefined, right?

-Kevin
 
K

Kevin Goodsell

Kevin said:
From the same section, paragraph 3:

3 In the first alternative (delete object), if the static type of the
operand is different from its dynamic type, the static type shall be a
base class of the operand's dynamic type and the static type shall
have a virtual destructor or the behavior is undefined.

I think that would cover void *, meaning the behavior is undefined, right?

Footnote from the section I quoted above:

21) This implies that an object cannot be deleted using a pointer of
type void* because there are no objects of type void.

-Kevin
 
W

WW

Kevin said:
So are you saying it can be a void *, or it cannot? As far as I can
tell, this passage only talks about the value of the operand, not the
type.

It can. And it is undefined behavior. IMO they could say it cannot. I
have no idea why didn't they do so. There is no DR on it... I wonder why.
But I guess at 00:30 with flu I will not find it out. :-( I guess we could
post to the std newsgroup to ask why didn't they say that deleting a void*
is ill-formed. g++ warns about it being undefined but that is all. I did
not see anything telling what the type should be.
 
W

WW

Kevin said:
3 In the first alternative (delete object), if the static type of
the operand is different from its dynamic type, the static type
shall be a base class of the operand's dynamic type and the
static type shall have a virtual destructor or the behavior is
undefined.

I think that would cover void *, meaning the behavior is undefined,
right?

Paragraph 2 with 1 together does. It should either be a (or be convertible
to) a pointer to a complete class type or to an array. void is neither of
those and as you see my quote then the behavior (for a pointer to non-array
what void * is) is undefined.
 
W

WW

Kevin said:
Footnote from the section I quoted above:

21) This implies that an object cannot be deleted using a
pointer of type void* because there are no objects of type void.

Yep. But footnotes are non-normative and the normative text only says
undefined behavior. I think deleting of void * should be ill-formed. It
can be vaught at compile time and it is always an error.
 
D

David B. Held

Kevin Goodsell said:
[...]
This would allow someone to write:

auto_ptr<int> p(new int);

delete p;

Would it? Can you delete a void *? The delete operator
wouldn't know what destructor (if any) to use, or the
amount of memory to be freed (unless it's stored with
the allocated memory somehow).

How does it know when you cast to void* and back?

int* p = new int(42);
void* q = static_cast<void*>(p);
int* r = static_cast<int*>(q);

delete r;

Obviously, the information must be saved somewhere,
so it doesn't seem impossible that:

delete q;

wouldn't have the same effect as "delete r;" above.

Anyway, I seem to recall that this was an argument for
not having an implicit conversion to void* in MC++D,
which is why Loki::SmartPtr does not provide one.
But, since my copy of MC++D is not handy, I can't
look it up.

Dave
 
K

Kevin Goodsell

WW said:
Yep. But footnotes are non-normative and the normative text only says
undefined behavior. I think deleting of void * should be ill-formed. It
can be vaught at compile time and it is always an error.

Well, my interpretation of paragraph 3 makes it ill-formed. But perhaps
my interpretation is wrong.

-Kevin
 
W

WW

Kevin said:
Well, my interpretation of paragraph 3 makes it ill-formed. But
perhaps my interpretation is wrong.

Unfortunately it is. The phrase ill-formed is nowhere present in that
paragraph (or the 1 and 2 for that matter). What is there is undefined
behavior. Which needs no diagnostics! So in this matter (as I understand
the current text and saw no DRs on it) David B. Held is right, the auto_ptr
class type would implicitly be converted into a void * and then all the
demons of hell would break loose when it is deleted. IMO this is a fixable
error in the standard. I see no reason why to leave this void* in as
undefined behavior, when it is clearly diagnostizable at compile time. I
also do not get the undefined behavior if the pointed class is incomplete -
but that can be justified. Only the static type can be checked at compile
time, the dynamic not. But IMO this is only problem with multiple
inheritance. With single inheritance the deletion can be done by simply
calling the right destructor, since there will be no subobject and no
pointer adjustment is necessary.
 
G

Gianni Mariani

David said:
[...]
This would allow someone to write:

auto_ptr<int> p(new int);

delete p;

Would it? Can you delete a void *? The delete operator
wouldn't know what destructor (if any) to use, or the
amount of memory to be freed (unless it's stored with
the allocated memory somehow).


How does it know when you cast to void* and back?

int* p = new int(42);
void* q = static_cast<void*>(p);
int* r = static_cast<int*>(q);

delete r;

Obviously, the information must be saved somewhere,
so it doesn't seem impossible that:

I think you're argument is lacking.

r is an int * - the size is sizeof(int). The static_cast<int*>(q) put
that information you're referring to back into the pointer. Hence the
size of what r is pointing to is known at the time it is deleted.

What do you think this should do ?

int* p = new char(42);
void* q = static_cast<void*>(p);
delete q;

the size of what q is pointing to is not known.
 
D

David B. Held

Gianni Mariani said:
[...]
What do you think this should do ?

int* p = new char(42);
void* q = static_cast<void*>(p);
MuthaObject * r = static_cast<int*>(q);
delete r;

Unless there is a defined conversion from int* to
MuthaObject*, I think it should elicit a diagnostic.
And if there is a conversion from int* to MuthaObject*,
I should hope the author of such a conversion has
taken into account this very situation.
the size of what q is pointing to is not known.

That's an interesting question. Theoretically, the
compiler *could* always keep track of what void* are
pointing to, but I'm not sure they are required to do so,
which is probably why it results in undefined behaviour.

Dave
 
R

Rob Williscroft

Gianni Mariani wrote in
I think you're argument is lacking.

r is an int * - the size is sizeof(int). The static_cast<int*>(q) put
that information you're referring to back into the pointer. Hence the
size of what r is pointing to is known at the time it is deleted.

What do you think this should do ?

int* p = new char(42);
void* q = static_cast<void*>(p);


the size of what q is pointing to is not known.

operator delete doesn't take size info, demo below.

The type info in delete is (can) only be used to call the destructor.
Also there would be little utility in banning delete (void *), as
all it really does is call the global operator delete( void * ). It
never needs the :: when used in a member function though.

#include <iostream>
#include <ostream>

struct base
{
virtual ~base() {}
void *operator new (std::size_t sz )
{
std::cout << "new; " << sz << std::endl;
return ::eek:perator new( sz );
}

void operator delete ( void *p )
{
std::cout << "deleteing; " << p << std::endl;
::eek:perator delete ( p );
}
};

struct derived: base
{
int i;
};

void del_base( base *b )
{
delete b;
}

int main()
{
base *b = new base;
del_base( new derived );
del_base( b );
}

Rob.
 
G

Gianni Mariani

David said:
[...]
What do you think this should do ?

int* p = new char(42);
void* q = static_cast<void*>(p);
MuthaObject * r = static_cast<int*>(q);
delete r;


Unless there is a defined conversion from int* to
MuthaObject*, I think it should elicit a diagnostic.

Maybe, but it is perfectly legal C++ (except for a typo I just noticed).

The point was that the pointer information is mostly compile-time, not
run time and with casting you get to control the behaviour.
And if there is a conversion from int* to MuthaObject*,
I should hope the author of such a conversion has
taken into account this very situation.

Even if there was a conversion, it would not be used because the
conversion is from a void *.

No question that the above code is like whacking yourself over the head
with a 4x2. C++ (and C) let's you do that, especially when you start
messing with type casts.
That's an interesting question. Theoretically, the
compiler *could* always keep track of what void* are
pointing to, but I'm not sure they are required to do so,
which is probably why it results in undefined behaviour.

I don't think you can. There are paths of data that are outside the
control of the conmpiler.
 
G

Gianni Mariani

Rob said:
Gianni Mariani wrote in



operator delete doesn't take size info, demo below.

I think it does.


#include <iostream>


char allocator_data[1024];
int amount_allocated = 0;


class Foo
{
public:

void * operator new( unsigned int size )
{
char * ptr = ( allocator_data + amount_allocated );
amount_allocated += size;

std::cout << "Allocate size is " << size << std::endl;

return ( void * ) ptr;
}

void * operator new[]( unsigned int size )
{
char * ptr = ( allocator_data + amount_allocated );
amount_allocated += size;

std::cout << "Allocate size is " << size << std::endl;

return ( void * ) ptr;
}

void operator delete( void * ptr, unsigned int size )
{
std::cout << "delete size is " << size << std::endl;

// drop memory on the floor
}

void operator delete[] ( void * ptr, unsigned int size )
{
std::cout << "delete size is " << size << std::endl;

// drop memory on the floor
}

};


class B : public Foo
{
public:
int xxx;
int yyy;
};


int main()
{

B * z = new B;

B * k = new B[30];

delete z;

delete [] k;
}

Admittedly it's an optional parameter BUT it is available and I've seen
some really KULE allocators that use this information when managing
extreme numbers of tiny objects and saving big time on memory and cycles.
The type info in delete is (can) only be used to call the destructor.

Most of the time.
Also there would be little utility in banning delete (void *), as
all it really does is call the global operator delete( void * ). It
never needs the :: when used in a member function though.


delete ptr

and

operator delete( ptr )

are 2 different things.

delete ptr also calls the destructor (if it exists) while operator
delete( ptr ) simply calls the method "operator delete( ... )".
 
D

David B. Held

Gianni Mariani said:
David said:
[...]
Unless there is a defined conversion from int* to
MuthaObject*, I think it should elicit a diagnostic.

Maybe, but it is perfectly legal C++ (except for a typo I
just noticed).

I'm not sure what's so legal about it:

class MuthaObject { };

int main()
{
MuthaObject* p = new int;
}

"ComeauTest.c", line 5: error: a value of type "int *" cannot
be used to initialize an entity of type "MuthaObject *"
MuthaObject* p = new int;

The point was that the pointer information is mostly
compile-time, not run time and with casting you get to
control the behaviour.

Of course. What does that have to do with void*?
Even if there was a conversion, it would not be used
because the conversion is from a void *.

Not sure what you're talking about here. I'm talking
about:
MuthaObject * r = static_cast<int*>(q);
[...]
That's an interesting question. Theoretically, the
compiler *could* always keep track of what void* are
pointing to, but I'm not sure they are required to do so,
which is probably why it results in undefined behaviour.

I don't think you can. There are paths of data that are
outside the control of the conmpiler.

Such as?

Dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top